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/cardNnode (typically groupvideo, or world-readable on many installs). - Privileges gained or impact: if the bound driver lacks
DRIVER_GEMand a configureddrm_ttm_bdev(or TTM init failed), anymmap(2)on the fd triggers an immediate kernel panic β a reliable local DoS with no authentication required (theEACCEScheck atdrm_vm.c:62is 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_GEMset, or whose TTM bdev init failed. No in-tree KMS driver currently exercises the path in normal operation (i915/radeon/amdgpu all setDRIVER_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/cardNwhose 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.
Recommended fix
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
sys/vm/device_pager.c:330,360β the onlyd_mmapcallers, both passingNULLfile pointer.sys/dev/drm/drm_drv.c:1255-1274βdrm_mmap_singlefallback gate.sys/dev/drm/drm_vm.c:57-60β in-source comment documenting the hazard.
Timeline
- 2026-07-25 Discovered during automated audit.
- 2026-07-25 Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2124 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
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
fixedbatch build rc=0
batch 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
No comments yet.