drm_gem_init: unchecked mm kzalloc (NULL-deref panic under kernel OOM), partial-failure leaks, and dangling dev->mm_private on the drm_ht_create failure path
Summary
drm_gem_init kzallocs mm and vma_offset_manager but NULL-checks only the latter; mm failure panics in drm_ht_create(&NULL->offset_hash). The !vma path leaks mm. The ht_create failure path leaves dev->mm_private dangling (assigned before, kfreed without clearing) - later dereferenced by drm_gem_destroy and every create_mmap_offset. Upstream FreeBSD checks both; the port dropped the check and reordered the assignment. OOM-gated module-load-time. Check-both + publish-last fix, compile-validated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4459 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 733 B | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| run.sh | β | 146 B | view raw | |
| build.sh | β | 143 B | view raw | |
| run.log | β | 1.1 KB | view raw | |
| build.log | β | 3.4 KB | view raw | |
| fix.diff | β | 692 B | view raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 3.1 KB | view raw | |
| env.txt | β | 630 B | view raw |
DF-4459 β drm_gem_init: unchecked mm kzalloc (NULL-deref panic under OOM), partial-failure leaks, and dangling dev->mm_private
Verify (static, against the audit tree)
sh run.sh sys/dev/drm/drm_gem.c # prints the asymmetry + dangling assignment
Build (fix compile-validation)
sh build.sh (inside guest, fixed drm_gem.c staged) β drm.ko rebuilds
-Werror clean, RC=0 (build.log).
Files
VERDICT.mdβ root cause with line cites, trigger analysisrun.sh+run.logβ static verification of the defect in-treebuild.sh+build.logβ patched module buildfix.diffβ NULL-check both allocations; assign dev->mm_private only after drm_ht_create succeeds; free both on failure
DF-4459 β PoC Verification Verdict
File: sys/dev/drm/drm_gem.c (pass 2), drm_gem_init() at
drm_gem.c:130-163.
Guest: DragonFly 6.5-DEVELOPMENT (X86_64_GENERIC), cc 8.3.
Verdict: INCONCLUSIVE at runtime (code defect Certain; OOM-gated)
Root cause
139: mm = kzalloc(sizeof(struct drm_gem_mm), GFP_KERNEL);
140: vma_offset_manager = kzalloc(sizeof(*vma_offset_manager), GFP_KERNEL);
141: if (!vma_offset_manager) { /* <-- mm is NOT checked */
142: DRM_ERROR("out of memory\n");
143: return -ENOMEM;
144: }
146: dev->mm_private = mm;
148: if (drm_ht_create(&mm->offset_hash, 12)) {
149: kfree(mm); /* <-- dev->mm_private dangles */
150: return -ENOMEM;
151: }
Three defects on the OOM paths:
- NULL-deref panic: if
kzalloc(mm)(139) fails whilekzalloc(vma_offset_manager)(140) succeeds, execution proceeds todrm_ht_create(&mm->offset_hash, 12)(148) β a NULL pointer offset dereference inside the drm hashtable create (&NULL->offset_hash, dereferenced by drm_ht_create) β kernel panic. Unprivileged users can drive the system into kernel-memory exhaustion (GFP_KERNEL allocations fail under pressure), but the allocation here happens at drm device registration (module load / device attach), so the practical trigger is memory pressure coinciding with a drm (re)load. - Memory leak: on the
!vma_offset_managerpath (141-144),mm(139) is leaked. - Dangling
dev->mm_private:dev->mm_private = mm(146) is assigned beforedrm_ht_createcan fail; the failure path kfreesmm(149) without clearingdev->mm_privateβ laterdrm_gem_destroy()runsdrm_mm_takedown(&mm->offset_manager)(170) anddrm_ht_remove(171) on freed memory (UAF), and everydrm_gem_create_mmap_offset_size()in between dereferences the stale pointer (drm_gem.c:570, 576-577). (vma_offset_manager is likewise leaked on this path.)
Upstream FreeBSD checks both allocations (if (!mm) β¦); the DragonFly
port dropped the mm check and reordered the assignment.
Reproduction
Not runtime-reproducible: the only trigger is kzalloc(GFP_KERNEL)
failure during drm device init β not injectable on the QEMU guest (no
fault-injection knob for this site, and no drm hardware; force-loading
drm.ko without hardware wedges the loader). Static verification of the
defect: run.sh (greps the tree, run.log); the data-flow is fully
cited above.
Fix validation
fix.diff checks both allocations, frees both on failure, and moves
dev->mm_private = mm after the drm_ht_create success point.
Compile-validated on the guest: drm.ko rebuilt with the patch,
-Werror, RC=0 (build.log; combined with the sibling DF-4458/DF-4460
hunks). git apply --check passes against the pristine tree.
fix_status=not_testable (no OOM injection / no drm hardware),
compile-validated.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff (git apply --check OK) checks both allocations, frees both on failure, and moves dev->mm_private assignment after drm_ht_create success. Compile-validated on the guest: drm.ko rebuilt with the fix, -Werror, RC=0 (build.log). Behavioral runs impossible: OOM-injection at this site is not available on the guest and there is no drm hardware (fix_status not_testable, compile-validated).
fix.diff; build.log (patched drm.ko full -Werror compile+link, DRM_BUILD_RC=0); VERDICT.md 'Fix validation'
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (grep script output confirming 139/141 asymmetry, 146-before-149 dangling assignment, 170-171 downstream derefs); build.log (drm.ko rebuilt with fix, -Werror, DRM_BUILD_RC=0); fix.diff (git apply --check OK)
PoC changes
no seed; static-verification script authored for the pack
Verified recommended fix
NULL-check both kzallocs, free both on any failure, and assign dev->mm_private only after drm_ht_create() succeeds
Verdict
Code defect certain, OOM-gated: drm_gem_init (drm_gem.c:130-163) kzallocs mm (139) and vma_offset_manager (140) but NULL-checks only vma_offset_manager (141). If mm's allocation fails, drm_ht_create(&mm->offset_hash, 12) (148) dereferences &NULL->offset_hash -> panic; unprivileged memory pressure can induce GFP_KERNEL failure but the site runs at drm device registration, so the practical window is OOM coinciding with a drm load. On the !vma_offset_manager path mm is leaked; on the drm_ht_create failure path dev->mm_private (assigned at 146, before the failure point) kfrees mm at 149 without being cleared, leaving a stale pointer that drm_gem_destroy (170-171) and every drm_gem_create_mmap_offset_size (570, 576-577) later dereference (UAF), and vma_offset_manager is leaked there too. Upstream FreeBSD checks both allocations; the port dropped the mm check and reordered the assignment. Not runtime-reproducible on the audit guest (no OOM injection site, no drm hardware; force-loading drm.ko without hardware wedges the loader) β defect proven by static verification (run.log) with all hops cited in VERDICT.md.
No comments yet.