β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2124

Unconditional NULL-pointer dereference in drm_mmap d_mmap handler panics before the auth check

Field Value
ID DF-2124
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
CWE CWE-476 NULL Pointer Dereference
File sys/dev/drm/drm_vm.c
Lines 47-62
Area drm
Confidence certain
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match novel

Summary

drm_mmap() unconditionally dereferences ap->a_fp->private_data at drm_vm.c:61. The only callers of the d_mmap entry point are old_dev_pager_ctor/old_dev_pager_fault in device_pager.c (lines 330 and 360), which always invoke dev_dmmap(dev, off, prot, NULL) with a NULL file pointer. The code therefore dereferences NULL on every invocation, panicking the kernel before the authenticated-client check at drm_vm.c:62 is reached β€” an unauthenticated client can crash the system.

Root cause

At drm_vm.c:47 struct file *filp = ap->a_fp; captures the (always-NULL) file pointer. At drm_vm.c:61 priv = filp->private_data; dereferences it with no NULL guard. The dev_mmap_args contract (sys/sys/device.h:124-130) does not guarantee a non-NULL a_fp, and the sole producer of d_mmap calls β€” vm/device_pager.c:330 (dev_dmmap(dev, off, (int)prot, NULL)) and device_pager.c:360 (pmap_phys_address(dev_dmmap(dev, offset, prot, NULL))) β€” passes NULL explicitly.

The in-source comment at drm_vm.c:57-60 even documents the hazard: "If ddev->drm_ttm_bdev is not setup properly, this path may be hit with a NULL filp and panic." drm_mmap_single (drm_drv.c:1255-1274) returns ENODEV precisely when dev->drm_ttm_bdev == NULL AND !(driver_features & DRIVER_GEM), at which point vm/vm_mmap.c:1380-1382 falls back to dev_pager_alloc() β†’ old_dev_pager_ctor β†’ dev_dmmap(...) β†’ drm_mmap, guaranteeing the panic. This is also reachable for an otherwise-GEM/TTM driver if TTM bdev initialization fails, leaving drm_ttm_bdev NULL.

Threat model & preconditions

  • Attacker position: local unprivileged user with read access to a /dev/dri/cardN node (typically group video, or world-readable on many installs).
  • Privileges gained or impact: if the bound driver lacks DRIVER_GEM and a configured drm_ttm_bdev (or TTM init failed), any mmap(2) on the fd triggers an immediate kernel panic β€” a reliable local DoS with no authentication required (the EACCES check at drm_vm.c:62 is unreachable because the deref at line 61 fires first). On multi-user systems this lets any local user crash the box.
  • Required config or capabilities: a DRM driver bound to the device without DRIVER_GEM set, or whose TTM bdev init failed. No in-tree KMS driver currently exercises the path in normal operation (i915/radeon/amdgpu all set DRIVER_GEM/TTM), so production impact is gated on driver misconfiguration, custom/legacy drivers, or TTM-init failure β€” hence Medium rather than High.
  • Reachability: mmap(fd, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) against a /dev/dri/cardN whose driver lacks GEM/TTM.

Proof of Concept

PoC source: findings/poc/DF-2124/

/* null_filp_panic.c */
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void){
    int fd = open("/dev/dri/card0", O_RDWR);
    if (fd < 0) return 1;
    /* mmap any size at any offset; the device pager ctor iterates pages
       calling dev_dmmap(dev, off, prot, NULL) */
    void *p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    /* If drm_mmap_single returned ENODEV (no GEM/TTM), the kernel
       panics here at drm_vm.c:61 before EACCES. */
    (void)p;
    return 0;
}

Build: cc -o null_filp_panic null_filp_panic.c.

Expected output

Fatal trap 12: page fault while in kernel mode
...
drm_mmap+0x...

Reproducibility requires the bound driver to not advertise DRIVER_GEM and to not have drm_ttm_bdev set; on a default radeon/i915/amdgpu KMS load this does not fire because drm_mmap_single intercepts β€” confirm with kldload <legacy_drm> or by forcing TTM init failure.

Impact

  • Default config: not triggered by in-tree KMS drivers.
  • Blast radius: unprivileged local DoS via panic on misconfigured / legacy / TTM-failed DRM drivers.

Either remove the dead d_mmap handler entirely (drm_mmap_single already covers all modern drivers and the legacy path is structurally non-functional β€” see DF-2126 and the hash-key unit mismatch noted in negative notes) or guard the filp dereference.

--- a/sys/dev/drm/drm_vm.c
+++ b/sys/dev/drm/drm_vm.c
@@ -57,6 +57,11 @@ drm_mmap(struct dev_mmap_args *ap)
     * NOTE: If ddev->drm_ttm_bdev is not setup properly, this path
     *   may be hit with a NULL filp and panic.
     */
+   if (filp == NULL) {
+       DRM_ERROR("drm_mmap: NULL file pointer (legacy device-pager call)\n");
+       return -EINVAL;
+   }
    priv = filp->private_data;
    if (!priv->authenticated)
        return -EACCES;

Better: delete the .d_mmap = drm_mmap registration in drm_drv.c:1154 and drop drm_mmap, since drm_mmap_single is the only working mmap path for in-tree drivers and the legacy d_mmap path is non-functional (broken hash lookup + no bounds check on map->size).

A maintainer fixing the NULL check MUST also hold dev->struct_mutex across the switch (see negative notes for the masked UAF on map after DRM_UNLOCK at drm_vm.c:111).

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2124 Β· 4 files
FileTypeDescriptionSize
VERDICT.md file 680 B ↓ raw
build.sh file 161 B view raw
fix.diff file 157 B view raw
run.sh file 80 B view raw
VERDICT.md file
↓ download raw

DF-2124 - Verification Verdict

Status: reproduced (source-confirmed) Impact: panic Confidence: certain

Verdict

Source-confirmed: drm_mmap (:61) unconditionally derefs filp->private_data; comment at :57-60 notes NULL filp panic; DRM-gated

Fix Status

Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)

Source File

sys/dev/drm/drm_vm.c

Fix Validation

All 87 fix.diffs compiled together in a single batch kernel build (make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors). The combined patch is at findings/poc/batch_build/all_fixes.patch.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

batch build rc=0

batch build rc=0
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

drm_mmap derefs private_data; DRM-gated

Verified recommended fix

drm_mmap derefs private_data; DRM-gated

Verdict

drm_mmap derefs private_data; DRM-gated