Skip to content

nvidia-drm: fix NULL deref in revoke_modeset_permission on alloc failure - #1280

Open
nvme0n1p1 wants to merge 1 commit into
NVIDIA:mainfrom
nvme0n1p1:fix/nvidia-drm-revoke-modeset-null-deref
Open

nvidia-drm: fix NULL deref in revoke_modeset_permission on alloc failure#1280
nvme0n1p1 wants to merge 1 commit into
NVIDIA:mainfrom
nvme0n1p1:fix/nvidia-drm-revoke-modeset-null-deref

Conversation

@nvme0n1p1

Copy link
Copy Markdown

Summary

nv_drm_revoke_modeset_permission() in kernel-open/nvidia-drm/nvidia-drm-drv.c
calls nv_drm_atomic_state_base_alloc() which can return NULL under memory
pressure. When the allocation fails, the error path jumps to the done label
and unconditionally calls nv_drm_atomic_state_base_put(state) with
state == NULL.

The underlying drm_atomic_state_put() / drm_atomic_commit_put() performs
kref_put(&state->ref, ...), dereferencing the NULL pointer.

Fix

Guard the nv_drm_atomic_state_base_put(state) call at the done: label with
a NULL check, so the put is only called when the allocation succeeded:

 done:
-    nv_drm_atomic_state_base_put(state);
+    if (state)
+        nv_drm_atomic_state_base_put(state);

The other two nv_drm_atomic_state_base_alloc() call sites in the driver
return early on allocation failure without calling put():

  • nvidia-drm-drv.c:999 (nv_drm_reset_input_colorspace)
  • nvidia-drm-helper.c:104 (nv_drm_atomic_helper_disable_all)

Unlike those sites, nv_drm_revoke_modeset_permission() acquires the modeset
lock via DRM_MODESET_LOCK_ALL_BEGIN before the allocation, so it cannot
return directly on failure — it must fall through to done: where
DRM_MODESET_LOCK_ALL_END releases the lock. The NULL guard is therefore the
correct minimal fix here rather than an early return.

Reproduction

Observed in the wild on 2026-08-09 under global OOM conditions. A large
parallel compilation job (hundreds of cc1plus processes) plus a 6.6 GB
QEMU VM exhausted 16 GB RAM. The kernel OOM killer killed several
processes including xdg-desktop-portal (PID 961, UID 1000).

During xdg-desktop-portal's exit, do_exit()__fput()
drm_release()drm_file_free()nv_drm_postclose() called
nv_drm_revoke_modeset_permission(). With memory fully exhausted,
nv_drm_atomic_state_base_alloc() returned NULL, the function jumped
to done:, and nv_drm_atomic_state_base_put(NULL) dereferenced the
NULL pointer:

BUG: kernel NULL pointer dereference, address: 0000000000000000
#PF: supervisor write access in kernel mode
#PF: error_code(0x0002) - not-present page
PGD 330a7e067 P4D 330a7e067 PMD 0
Oops: Oops: 0002 [#1] SMP NOPTI
CPU: 4 UID: 1000 PID: 961 Comm: xdg-desktop-por Tainted: G           OE       7.1.5-zen1-2-zen #1 PREEMPT(full)  a89a67a7c5569151d29b0c559224dd9197ba11c6
Tainted: [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
Hardware name: LENOVO 82B6/LNVNB161216, BIOS EUCN41WW 09/20/2023
RIP: 0010:nv_drm_revoke_modeset_permission+0xfa/0x410 [nvidia_drm]
Code: ee 4c 89 e7 e8 c7 76 62 ce 85 c0 0f 85 29 01 00 00 4c 89 e7 e8 37 e9 5e ce 48 89 c5 48 85 c0 0f 85 33 01 00 00 b8 ff ff ff ff <f0> 0f c1 04 25 00 00 00 00 83 f8 01 0f 84 75 02 00 00 85 c0 0f 8e
RSP: 0018:ffffd36101b77c18 EFLAGS: 00010246
RAX: 00000000ffffffff RBX: ffff8a2d8af89e00 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffffc13f59f3 RDI: 0000000000000000
RBP: 0000000000000000 R08: ffff8a2d82480f40 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff8a2d96358000
R13: ffffd36101b77c38 R14: ffff8a2d8af89e01 R15: ffff8a2d96358000
FS:  0000000000000000(0000) GS:ffff8a310cc03000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 0000000122a56000 CR4: 0000000000350ef0
Call Trace:
 <TASK>
  drm_file_free+0x238/0x2a0
  drm_release+0xc3/0x150
  __fput+0x105/0x2e0
  task_work_run+0x66/0xa0
  do_exit+0x30c/0xba0
  get_signal+0x918/0x9d0
  arch_do_signal_or_restart+0x77/0x2d0
  irqentry_exit+0x209/0x740
  ? exc_page_fault+0x90/0x1d0
  asm_exc_page_fault+0x26/0x30
 </TASK>
note: xdg-desktop-por[961] exited with irqs disabled
Fixing recursive fault but reboot is needed!

The faulting instruction (lock xadd eax, DWORD PTR [0x0], marked with
<f0>) is the kref_put() inside
drm_atomic_state_put()/drm_atomic_commit_put(), operating on
state->ref at offset 0 with state == NULL (RBP=0, RDI=0, CR2=0,
RAX=0xffffffff=-1).

Environment:

  • Kernel: 7.1.5-zen1-2-zen (PREEMPT full)
  • Driver: 610.57.04 (nvidia-drm, open modules)
  • Hardware: LENOVO 82B6, BIOS EUCN41WW
  • Memory at crash: 16 GB RAM, Normal zone free ~24 MB,
    all_unreclaimable? yes, swap cache ~292k pages

This is reachable from any unprivileged process that holds a DRM fd
(e.g. xdg-desktop-portal, any GPU-accelerated app) during OOM — no
DRM_MASTER required, no ioctl needed, just closing the fd.

Testing

  • git apply --check confirms the patch applies cleanly on 610.57.04.
  • Code audit confirms all three code paths reaching the done: label:
    • OOM path (state == NULL): now correctly skips put().
    • nv_drm_atomic_disable_connector() failure (state != NULL):
      unchanged, still calls put().
    • Normal drm_atomic_commit() path (state != NULL): unchanged.
  • The fix aligns nv_drm_revoke_modeset_permission() with the
    error-handling pattern used at the other two alloc sites in the driver.
  • Reproducible at runtime via kernel fault injection
    (CONFIG_FAULT_INJECTION + /sys/kernel/debug/fail_page_alloc), which
    forces nv_drm_atomic_state_base_alloc() to return NULL on fd close.

Impact

Local denial of service (kernel panic). Requires:

  • Local access to DRM device node (/dev/dri/card*, typically video
    group or any GPU-accelerated desktop process)
  • Memory pressure / OOM conditions to trigger allocation failure

Confirmed by a real-world crash. No code execution, no privilege
escalation, no information disclosure.

When nv_drm_atomic_state_base_alloc() fails in
nv_drm_revoke_modeset_permission() (e.g. under memory pressure), the
error path jumps to the 'done' label where it unconditionally calls
nv_drm_atomic_state_base_put(state) with state == NULL.  The underlying
drm_atomic_state_put()/drm_atomic_commit_put() performs kref_put on
state->ref, dereferencing the NULL pointer and crashing the kernel.

Guard the put() call with a NULL check.  This is consistent with the
other nv_drm_atomic_state_base_alloc() call sites in the driver
(nvidia-drm-drv.c:999 and nvidia-drm-helper.c:104) which return early
on allocation failure without calling put().

Reachable from nv_drm_postclose() (any process closing a DRM fd) and
from the DRM_IOCTL_NVIDIA_REVOKE_PERMISSIONS ioctl, so a local user
with access to /dev/dri/card* can trigger a kernel panic under OOM
conditions.  Confirmed by a real-world crash during global OOM on
2026-08-09.
@CLAassistant

CLAassistant commented Aug 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants