DF-4791 / verdict.json
{ "finding_id": "DF-4791", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Structurally proven but not runtime-reproducible on the audit guest: ttm_mem_global_alloc_zone (ttm_memory.c:502-521) re-enters ttm_shrink (ttm_memory.c:517) with no recursion guard whenever ttm_bo_swapouts move-to-system step (ttm_bo.c:1807-1819, ctx no_wait_gpu=false at :1809) must lazily populate the victim BOs ttm pages (ttm_bo.c:344 -> ttm_tt.c:341 -> ttm_page_alloc.c:910 -> ttm_mem_global_alloc_page). Nesting depth equals the run of unpopulated-tt VRAM BOs an unprivileged /dev/dri client parks on swap_lru (ttm_bo.c:182-187), ~1-2KiB of kernel stack per level against the 16KiB x86_64 kernel stack (UPAGES=4) -> kernel stack overflow / adjacent-kernel-memory corruption. The audit guest has no DRM device (QEMU Bochs VGA chip 0x1234:1111, no /dev/dri, no drm modules), so the trigger exits 2 at open(); the PoC builds cleanly on the guest. Upstream removed this entire accounting shrinker in 2021 (b12dacf5fa64 drm/ttm: nuke memory accounting shrinker), corroborating that the mechanism is unsound. impact recorded as none strictly for the guest runtime observation; the source-level classification is local kernel-memory-corruption/DoS (High) on GPU-equipped machines.", "exploit_chain": "", "evidence": "VERDICT.md (full path:line recursion chain); run.log (build OK on guest, open ENOENT - feature absent); env.txt (no /dev/dri, no drm modules, Bochs VGA); fix.diff (bounded-nesting guard, advisory)", "kernel_refs": [ "sys/dev/drm/ttm/ttm_memory.c:517", "sys/dev/drm/ttm/ttm_memory.c:213", "sys/dev/drm/ttm/ttm_memory.c:502", "sys/dev/drm/ttm/ttm_bo.c:1807", "sys/dev/drm/ttm/ttm_bo.c:1809", "sys/dev/drm/ttm/ttm_bo.c:182", "sys/dev/drm/ttm/ttm_bo.c:344", "sys/dev/drm/ttm/ttm_tt.c:341", "sys/dev/drm/ttm/ttm_page_alloc.c:910", "sys/cpu/x86_64/include/param.h:126" ], "poc_changes": "PoC authored fresh (no seed): had to inline Linux-style drm ioctl encodings because the DFly guest ships no drm uapi headers and its _IOWR is the 3-arg BSD form; second build attempt succeeded.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026\troot@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC\tx86_64", "runtime_sec": 25, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o ttm_shrink_recursion ttm_shrink_recursion.c", "run_cmd": "./ttm_shrink_recursion 64 268435456", "code_hash": "8e8b8b76fda7aeb7901e04b486fa8264e27e4b78114f47d75468381a3fb39846", "notes": "Feature-absent on guest (no radeon/amdgpu/i915 hardware; QEMU Bochs VGA not driven by any in-tree drm driver). ttm_bo_swapout returns -EBUSY when swap_lru is drained (ttm_bo.c:1771,1787) so each shrinks while-loop terminates; it is the cross-call NESTING that is unbounded. Upstream fix reference: b12dacf5fa64.", "recommended_fix": "Cap ttm_shrink nesting depth in ttm_memory.c (bounded guard, see fix.diff); long-term follow upstream b12dacf5fa64 and remove the accounting shrinker.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "No DRM device on the audit guest, so neither baseline nor patched kernel can execute the trigger; fix.diff is validated by inspection only (guard increments outside glob->lock, caps depth at 4 x ~2KiB well within the 16KiB stack).", "fix_evidence": "fix.diff in this directory; guest hardware inventory in env.txt (Bochs VGA, no /dev/dri)." } |