DF-5105 / verdict.json
{ "finding_id": "DF-5105", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "certain", "verdict": "The leak is certain from source: a successful iteration of the move-test loop continues past the error-only unwind labels (amdgpu_test.c:217 vs 219-233), and the epilogue releases only vram_obj plus the pointer array (236-242), so all n GTT BOs \u2014 each created (92), reserved (98) and pinned (101), refcount held solely by the kfreed array \u2014 leak with their reservation locks locked forever; n is by construction (47-55) essentially the whole GTT. Runtime verification was genuinely attempted but the QEMU guest has no AMD display adapter (pciconf shows only the emulated bochs VGA chip 0x11111234): amdgpu.ko loads (kldload rc=0) but no device attaches, so amdgpu_device_init() and thus amdgpu_test_moves() (amdgpu_device.c:2684-2686) never execute here \u2014 hence not_reproduced/impact=none ON THIS GUEST. Realistic ceiling on AMD-equipped hosts: one root-gated (amdgpu_testing load-time param, amdgpu_drv.c:85/192) successful test run permanently consumes ~all GTT and leaves ~n ww_mutexes held -> graphics-stack DoS and wedged unload. No memory-corruption primitive (objects are leaked, never used-after-free), so no escalation chain exists for this class.", "exploit_chain": "", "evidence": "VERDICT.md (line-accurate derivation); run.log (probe output: no AMD VGA in pciconf, kldload amdgpu rc=0 with kernel modesetting enabled but zero device-attach lines); amdgpu_test.c:217 continue vs 219-233 error-only unwind and 236-242 epilogue freeing only vram_obj + kfree(gtt_obj) of the pointer array; fix.diff applies cleanly (git apply --check OK) and is a no-op on the error path because amdgpu_bo_unref NULLs slots", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_test.c:47-55", "sys/dev/drm/amd/amdgpu/amdgpu_test.c:85-101", "sys/dev/drm/amd/amdgpu/amdgpu_test.c:217", "sys/dev/drm/amd/amdgpu/amdgpu_test.c:219-233", "sys/dev/drm/amd/amdgpu/amdgpu_test.c:236-242", "sys/dev/drm/amd/amdgpu/amdgpu_device.c:2684-2686", "sys/dev/drm/amd/amdgpu/amdgpu_drv.c:85", "sys/dev/drm/amd/amdgpu/amdgpu_drv.c:192", "sys/dev/drm/amd/amdgpu/Makefile:63" ], "poc_changes": "No seed PoC existed; authored probe.sh fresh (kernel-internal path, so the PoC is a reachability probe: pciconf + kldload + dmesg, all captured untrimmed in run.log)", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 180, "guest_dirty": 0, "build_cmd": "./build.sh (no-op; nothing to compile)", "run_cmd": "dfbsd-qemu/vm.sh run_root pciconf -l; kldload amdgpu; kldstat; dmesg (full transcript in run.log)", "code_hash": "ab37f86d58a5290299885d2de57315a2af033590c07c9965ba06d59eb1f2d29a", "notes": "Known DF-2115 (double dma_fence_put, lines 140-232) not re-reported; this leak is the complementary success-path defect of the same epilogue. Killed candidates this pass: no ib/ring-test code in this file; no sysfs here (module_param lives in amdgpu_drv.c); n-underflow and kcalloc(0) both fail safe; stale gtt_map/vram_map after kunmap are compared by value only, never dereferenced; error-label chain (out_lclean_unpin/_unres/_unref/out_lclean) verified correct for every goto site. kldunload of the 65MB module exceeded the 120s probe budget; guest verified healthy afterwards (run_user id -> uid=1001).", "recommended_fix": "Add a post-loop guarded unwind of gtt_obj[] (unpin/unreserve/unref for every non-NULL slot) before kfree(gtt_obj) \u2014 see fix.diff, git-apply-able, no-op on the error path", "fix_status": "not_testable", "fix_baseline_reproduced": false, "fix_patched_reproduced": false, "fix_verdict": "not_testable: single-fix kernel build + PoC re-run is meaningless on a guest with no AMD GPU \u2014 the code path cannot be exercised in either the baseline or the patched kernel; the fix is validated structurally (guard makes it a no-op on the error path where slots are already unreffed/NULLed by amdgpu_bo_unref, and handles n==0 via i<0)", "fix_evidence": "fix.diff in this pack passes git apply --check against the unmodified sys/ tree; error-path no-op argument documented in VERDICT.md" } |