i915 GEM shrinker is dead code: never registered with any memory-reclaim path β purgeable/unbound GPU buffer pages are never reclaimed under system memory pressure (DF-4507 family)
Summary
The DFly shim has no register_shrinker at all (struct-only header), so the registration block and the count/scan bodies are #if 0; vm_lowmem (the only VM-pressure event in the DRM tree) has 0 EVENTHANDLER_INVOKE sites. Unbound/purgeable GEM shmem is invisible to reclaim: under pressure the kernel OOM-kills instead of purging memory userspace explicitly marked DONTNEED - reliable local availability DoS by any video-group user on i915 hw. Binary-proven on guest (nm: count/scan local dead stubs, no register_shrinker in i915.ko/drm.ko); runtime DoS not manifestable (std-VGA only). The i915 instance of the DF-4507 family. Feature-sized fix: shim register_shrinker + real VM-pressure event.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4819 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| check_shrinker_wiring.sh | β | 3.4 KB | view raw | |
| run.log | β | 768 B | view raw | |
| run.guest.log | β | 828 B | view raw | |
| env.txt | β | 369 B | view raw | |
| code_hash.txt | β | 91 B | view raw | |
| build.sh | β | 94 B | view raw | |
| run.sh | β | 204 B | view raw | |
| README.md | β | 2.8 KB | β raw | |
| VERDICT.md | β | 6.3 KB | β raw | |
| manifest.json | β | 1.4 KB | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-4819 β i915 GEM shrinker is never registered with any reclaim path (DF-4507 family)
What this is
Structural verification that the i915 GEM shrinker in the DragonFly DRM port can never run from system memory reclaim:
sys/dev/drm/include/linux/shrinker.h(the DFly linux-compat shim) declares onlystruct shrinker+SHRINK_STOPβregister_shrinkerdoes not exist at all in the shim (or anywhere insys/).- The i915 port compensates by
#if 0-ing out every registration call site:register_shrinker,register_oom_notifier,register_vmap_purge_notifier(sys/dev/drm/i915/i915_gem_shrinker.c:521-532). - The
i915_gem_shrinker_count/i915_gem_shrinker_scanbodies are#if 0stubs thatreturn 0(lines 313-351, 353-395); even the function pointers installed byi915_gem_shrinker_register()point at dead stubs that nothing will ever call. - The only global pressure hook that exists in the DRM tree
(
vm_lowmem, registered bysys/dev/drm/ttm/ttm_page_alloc.c:447) is neverEVENTHANDLER_INVOKE'd anywhere in the kernel (0 sites insys/).
Net effect: an unprivileged user with /dev/dri access on i915 hardware can
hold gigabytes of shmem-backed GEM pages (including I915_MADV_DONTNEED
"purgeable" objects that userspace explicitly marked reclaimable) that the VM
subsystem cannot reclaim under memory pressure β the box goes to the OOM
killer instead of purging GPU memory. Same defect family as DF-4507 (TTM page
pools) and complements it: DF-4507 = pool pages, DF-4819 = i915 GEM object
pages.
Build
Nothing to compile β this is a code-structure defect. The verification is a shell script:
./build.sh # no-op (explains why)
Run
./run.sh # static proof against the workspace sys/ tree # binary proof on a DragonFly guest: sh check_shrinker_wiring.sh --module /boot/kernel/i915.ko
Expected output
- Host static: six
PASSlines β noregister_shrinkerin shim; registration#if 0'd; count/scan bodies#if 0(return 0);vm_lowmeminvoked 0 times; livei915_gem_shrinkcallers are explicit only (get_pages failure paths at i915_gem.c:2935, i915_gem_gtt.c:2449, freeze paths at i915_gem.c:6324/6353). - Guest binary:
nm i915.koshowsi915_gem_shrinker_count/scanas local (t) stubs and noregister_shrinkersymbol/undefined reference;nm drm.koshows no such symbol either.
Runtime impact demonstration
Not possible on this audit guest: it runs QEMU std VGA (chip=0x11111234),
i.e. no Intel GPU, so i915 never attaches (kldstat clean). The DoS impact
(unreclaimable GPU memory under pressure) requires real i915 hardware.
This matches the precedent set by DF-4507 (recorded untested for the same
reason). The code-level defect itself is certain β proven statically and
against the compiled module binary.
VERDICT β DF-4819
Finding: i915 GEM shrinker is dead code in the DragonFly port β never registered with any memory-reclaim path β so purgeable/unbound GPU buffer object pages are never reclaimed under system memory pressure (OOM-killer DoS). DF-4507 family (that finding covered TTM page pools; this one covers i915 GEM object pages).
Structural verdict: DEFECT CONFIRMED (certain). Proven at three levels:
- Shim level β
sys/dev/drm/include/linux/shrinker.hcontains onlystruct shrinker { count_objects; scan_objects; seeks; },struct shrink_control,DEFAULT_SEEKS,SHRINK_STOP. There is noregister_shrinker, noregister_oom_notifier, noregister_vmap_purge_notifieranywhere in the shim or in all ofsys/. - Driver level β
sys/dev/drm/i915/i915_gem_shrinker.c: -i915_gem_shrinker_register()(lines 516-533) only assigns function pointers andseeks; the actual registrations at lines 521-532 are inside#if 0. -i915_gem_shrinker_count()(313-351) andi915_gem_shrinker_scan()(353-395) bodies are#if 0; both compile toreturn 0stubs. - The OOM notifieri915_gem_shrinker_oomand vmap purge notifieri915_gem_shrinker_vmapare entirely#if 0'd (397-508). -i915_gem_shrinker_unregister()(541-548) is an empty#if 0body. - Kernel level β
vm_lowmem(the one pressure event the DRM tree knows about, registered atsys/dev/drm/ttm/ttm_page_alloc.c:447) is neverEVENTHANDLER_INVOKE'd anywhere insys/kern,sys/vm,sys/sysor elsewhere (grep: 0 sites) β exactly as DF-4507 documented.
Binary confirmation on the guest (run.guest.log, DragonFly
6.5-DEVELOPMENT X86_64_GENERIC, 2026-06-29 build):
$ nm /boot/kernel/i915.ko | grep -iE 'shrinker|register_shrinker' 0000000000050cb0 t i915_gem_shrinker_count <- local stub, dead body 00000000000512a0 T i915_gem_shrinker_register <- assigns ptrs nobody calls 0000000000050cc0 t i915_gem_shrinker_scan <- local stub, dead body ... PASS i915.ko has NO register_shrinker reference PASS drm.ko (shim) has NO register_shrinker symbol at all
Why the impact is not demonstrated at runtime: the audit guest is a
QEMU/KVM VM with the emulated std VGA device (vgapci0 ... chip=0x11111234)
β no Intel GPU, so i915 never attaches (kldstat shows no drm/i915
modules). Reproducing the DoS (unprivileged user pins N GB of GEM shmem,
driver marks it available-for-reclaim, system memory pressure hits, kernel
OOM-kills processes instead of purging) requires real i915 hardware, which no
QEMU configuration in this harness provides (no i915 emulation exists
upstream). Recorded as status=untested, reproduced=0, impact=none
consistent with the DF-4507 family precedent in poc_results; the code-level
claim itself is confidence=certain.
The live code that does run was audited and is sound:
i915_gem_shrink() (145-280) is reachable, but only via explicit call sites:
- i915_gem.c:2935 β shmem page-alloc failure retry inside
i915_gem_object_get_pages_gtt (first pass purgeable, second pass nothing);
- i915_gem_gtt.c:2449 β i915_gem_gtt_prepare_pages() dma-map failure
retry loop (bounded: shrinks other objects until i915_gem_shrink returns
0, then -ENOSPC);
- i915_gem.c:6324 / 6353 β suspend/hibernate (i915_gem_freeze,
i915_gem_freeze_late); plus an I915_SELFTEST_ONLY fault-injection call
in i915_gem_gtt.c:417.
Pass-2 sweep of that live walk β all clean:
- shrinker_lock() (39-65): mutex_trylock_recursive() maps to DFly
lockowned(), which for exclusive locks tests lk_lockholder == curthread
(sys/kern/kern_lock.c:1365-1380) β the RECURSIVE case genuinely means
"current thread holds it", so the walk never proceeds without holding
struct_mutex. (struct_mutex is only ever taken LK_EXCLUSIVE in DRM.)
- The FAILED-case busy-wait is bounded by need_resched() β
any_resched_wanted() (real in DFly); worst case short CPU burn, no hang.
- LRU walk (211-268): objects are moved to a stack-local still_in_list
under mm.obj_lock; the lock is dropped (250) only while
struct_mutex is held throughout (163), and final-unref/free_work is
serialized by struct_mutex, so obj cannot be freed across the drop β
scanned += obj->base.size >> PAGE_SHIFT (262) is safe. list_splice_tail
(266) is empty-list safe (_list_splice returns early on empty,
sys/dev/drm/include/linux/list.h). No self-deadlock: the object being
shrunk is skipped because can_release_pages() requires
i915_gem_object_is_shrinkable() and the in-progress object has
mm.pages == NULL at that point.
- Arithmetic: count/scanned are unsigned long (u64); target can be
-1UL; page sums cannot wrap in practice; can_release_pages()
compares atomic_t > unsigned int correctly after integer promotion.
- i915_gem_shrinker_taints_mutex() (550-559): early-returns because
CONFIG_LOCKDEP is never defined in the DFly build β no-op.
Recommended fix (feature-sized, not a one-liner): implement a real
register_shrinker() in sys/dev/drm/include/linux/shrinker.h backed by a
DFly VM-pressure mechanism (e.g. a vm_lowmem event actually
EVENTHANDLER_INVOKE'd from vm/pageout, or a dedicated kmem-reclaim
callback), then remove the #if 0 from the registration block
(i915_gem_shrinker.c:521-532) and from the count/scan bodies (313-395),
adapting spin_lock β lockmgr in the restored count as the scan already
does. fix.diff is intentionally omitted: a verifiable fix requires new VM
infrastructure plus i915 hardware to validate against, not a patch this
evidence pack could honestly test on the available guest.
Artifacts: check_shrinker_wiring.sh (verification script, two modes),
run.log (host static proof, 6ΓPASS), run.guest.log (guest binary proof +
runtime context), env.txt, code_hash.txt, README.md, this file,
manifest.json, verdict.json.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableFix validation not possible: the fix requires new kernel VM infrastructure plus real i915 hardware to validate; the QEMU audit guest has no Intel GPU and the DoS baseline cannot be reproduced there in the first place.
n/a
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: six PASS lines proving no registration path exists in the workspace sys/ tree; run.guest.log: nm of /boot/kernel/i915.ko shows i915_gem_shrinker_count/scan as local (t) stubs and no register_shrinker symbol in i915.ko or drm.ko, plus guest context (uname, QEMU std VGA, no drm modules loaded); VERDICT.md: full per-line narrative incl. pass-2 sweep of the live i915_gem_shrink walk (lockowned==curthread at kern_lock.c:1365, struct_mutex lifetime invariant, empty-list-safe splice, no self-deadlock, u64 arithmetic sound).
PoC changes
No seed PoC existed for this pass-2 finding; authored check_shrinker_wiring.sh (dual-mode: static tree proof + compiled-module nm proof). Base64 transfer to guest failed (DFly lacks base64(1)); ran the module-mode checks inline via vm.sh run_root instead β same commands, captured verbatim in run.guest.log.
Verified recommended fix
Implement register_shrinker in sys/dev/drm/include/linux/shrinker.h wired to a VM-pressure event that DragonFly actually invokes (e.g. make vm_pageout EVENTHANDLER_INVOKE(vm_lowmem,...)), then un-#if0 the i915 registration block and the count/scan bodies (converting spin_lock to lockmgr in the restored count callback).
Verdict
Structural defect CONFIRMED at source and binary level: the DFly linux-compat shim has no register_shrinker (sys/dev/drm/include/linux/shrinker.h is struct-only), the i915 registration block and the count/scan bodies are #if 0'd out (i915_gem_shrinker.c:313-395, 521-532), and vm_lowmem is never EVENTHANDLER_INVOKE'd anywhere in sys/ β so the i915 GEM shrinker can never run from memory reclaim. Verified statically (run.log, 6x PASS) and against the guest's compiled i915.ko/drm.ko symbol tables (run.guest.log: no register_shrinker symbol; count/scan are local dead stubs). The DoS impact (GPU shmem pages unreclaimable under pressure -> OOM kills) requires real i915 hardware, which the QEMU audit guest lacks (std VGA 1234:1111, i915 never attaches) β recorded untested/impact-none consistent with the DF-4507 family precedent; potential impact is local DoS (Medium).
No comments yet.