DF-4924 / verdict.json
{ "finding_id": "DF-4924", "status": "not_reproduced", "reproduced": 0, "impact": "dos", "confidence": "certain", "verdict": "gfx_v9_0 gfx-ring emit_frame_size (gfx_v9_0.c:4745-4764, = 160 dw worst case with soc15.h constants 4/1) has no term for the 2-dword PACKET3_FRAME_CONTROL TMZ emission that amdgpu_ib_schedule() makes unconditionally whenever ring->funcs->emit_tmz is set (gfx_v9_0.c:4780 + amdgpu_ib.c:231-232). amdgpu_ring_alloc() aligns the reservation to 256 dw (align_mask 0xff), so the reservation has zero slack exactly when (160 + 4*num_ibs) % 256 == 0, i.e. num_ibs == 24 (mod 64); the TMZ dwords then write past the booked window. amdgpu_ring_write() (amdgpu_ring.h:276-283) DRM_ERRORs and writes anyway, so every such submission also spams the log and drifts wptr into the next submissions space; at ring wrap the 2 dwords can land on unconsumed commands (GPU command-stream corruption -> ring hang). Writes are masked inside the ring BO, so this is NOT a kernel-memory OOB \u2014 DoS-class ceiling from any unprivileged DRI client (num_ibs = number of AMDGPU_CHUNK_ID_IB chunks, amdgpu_cs.c:181-183). Kernel path is hardware-gated on this guest (Bochs VGA, no AMD device, no /dev/dri \u2014 env.txt), same situation as DF-4864; the arithmetic/write pattern is replicated byte-exactly by the harness (run.log: 2 canary dwords destroyed iff num_ibs in {24,88,152} of 1..200; fixed accounting: 0).", "exploit_chain": "On real Vega10/Raven hardware: unpriv DRI client -> CS ioctl with num_ibs == 24 (mod 64) IB chunks targeting the gfx ring -> amdgpu_ib_schedule reserves ALIGN(160+4n,256) dwords, emits 162+4n -> 2 dwords past the window per submission (accumulating) + DRM_ERROR spam; near ring wrap the stray dwords overwrite unconsumed commands -> GPU command corruption -> ring hang / device reset (local DoS). No kernel-memory write primitive (buf_mask contains writes), so no uid0 chain.", "evidence": "run.log lines num_ibs= 24 reserved= 256 emitted= 258 writes_past_count=2 canary_dwords_destroyed=2 (and n=88/152), fixed: 0 overrun submission(s), final VERDICT: ... REPLICATED.; run.2.log identical; fix_build.log (patch hunk @4759, full amdgpu.ko -Werror build EXIT=0, gfx_v9_0.o in final link); env.txt (guest has no AMD GPU); VERDICT.md arithmetic + revert hashes.", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_ib.c:159-166", "sys/dev/drm/amd/amdgpu/amdgpu_ib.c:231-232", "sys/dev/drm/amd/amdgpu/gfx_v9_0.c:4745-4764", "sys/dev/drm/amd/amdgpu/gfx_v9_0.c:4780", "sys/dev/drm/amd/amdgpu/amdgpu_ring.c:64-76", "sys/dev/drm/amd/amdgpu/amdgpu_ring.h:276-283", "sys/dev/drm/amd/amdgpu/soc15.h:31-32", "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:181-183" ], "poc_changes": "No seed PoC existed (pass-2 discovery); harness authored from scratch, constants transcribed from the tree with path:line comments; first draft had inverted ring_write indexing (caught by review, fixed before the guest run).", "attempts": 3, "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": 95, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -O2 -o df4924_trigger df4924_trigger.c, guest)", "run_cmd": "sh run.sh (guest)", "code_hash": "c6cdfb7a87948b585cc6a67a0ea7f62bc74e825fe2b9fe5e9a828546828036e0", "notes": "Hardware-gated: needs AMD gfx9 ASIC (Vega10/Raven). Surfaced during the pass-2 audit of amdgpu_ib.c (hunt item b: emit counts vs reserved, DF-4864 family) \u2014 the reservation contract is consumed at amdgpu_ib.c:159-162, the unaccounted emission is ib.c:231-232, the missing term lives in gfx_v9_0.c. Upstream-parity unverified (offline audit env). Adjacent observations NOT filed (other files seats): amdgpu_job.c:186-189 sync_fence error handling is safe (fence still returned to drm_sched as fallback wait); gfx_v8_0.c:897/gfx_v9_0.c:454 ring_test_ib writes 5 dwords (20 B) into a 16-B SA request (boot-time only, empty pool); amdgpu_cs.c parse_cs rings validate an SA copy while the GPU executes the user BO original (TOCTOU shape, upstream-identical).", "recommended_fix": "Add the missing TMZ term to gfx_v9_0.c emit_frame_size: 2 + /* FRAME_CONTROL (TMZ) */ (fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src (hunk @4759 clean), full amdgpu.ko module rebuilt under -Werror EXIT=0 with gfx_v9_0.o in the final link (fix_build.log); harness A/B: baseline 3 overruns (n=24/88/152) vs fixed 0 overruns (run.log); runtime kernel A/B impossible on this guest (no AMD GPU); source reverted, sha256 back to baseline 119af8855ea0b484531066ee0a13cbc2c8005920abae895488991ff9d8a1a67c.", "fix_evidence": "findings/poc/DF-4924/fix_build.log (patch + cc -r -o amdgpu.ko ... gfx_v9_0.o EXIT=0); run.log == fixed accounting == section (0 overruns); VERDICT.md Fix validation section." } |