DF-5148 / verdict.json
{ "finding_id": "DF-5148", "status": "untested", "reproduced": false, "impact": "none", "confidence": "certain", "verdict": "Real, certain-from-code-shape off-by-one in the AUX completion poll of radeon_dp_aux_transfer_native(): the do-while at radeon_dp_auxch.c:152-158 executes its body with retry_count==0..1000 (1001 executions), so a hardware AUX_SW_DONE observed during the 1001st poll breaks the loop with retry_count==1000 and the counter-keyed gate at :160 then fails a COMPLETED transfer with -EIO (auxch hw never signalled completion). The control-flow arithmetic was executed and proved on the audit guest with loop_offbyone.c (run.log: shipped gate -5/EIO vs fixed gate 0 for DONE-at-rc=1000; identical classification for all other stimuli). On-hardware verification is impossible in the audit guest: no AMD/ATI PCI device exists there (env.txt: pciconf shows QEMU emulated VGA only), so the radeon driver cannot attach and the MMIO poll cannot run; per contract Info-class findings skip QEMU Phase V - hence status untested, not a refutation. No memory-safety impact: the misclassified path takes the normal goto done cleanup; callers (drm_dp_aux core) retry aux transactions, so the effect is a rare transient DP aux failure. Related known pass-1 findings DF-2146/DF-2147 cover the reply-count OOB and the aux_offset index and are not part of this finding.", "exploit_chain": "", "evidence": "run.log lines 3-6: DONE on 1001st poll (rc=1000) <-- last body -5 0 0 BUG: completed xfer misreported as -EIO and RESULT: shipped loop misclassifies a DONE-on-last-poll transfer as -EIO (radeon_dp_auxch.c:160); VERDICT.md traces the loop semantics; env.txt proves guest lacks radeon HW (no AMD/ATI PCI device)", "kernel_refs": [ "sys/dev/drm/radeon/radeon_dp_auxch.c:66", "sys/dev/drm/radeon/radeon_dp_auxch.c:152", "sys/dev/drm/radeon/radeon_dp_auxch.c:158", "sys/dev/drm/radeon/radeon_dp_auxch.c:160", "sys/dev/drm/radeon/radeon_dp_auxch.c:194", "sys/dev/drm/include/linux/delay.h:55" ], "poc_changes": "Harness authored fresh (no seed existed for this finding); no guest kernel changes; userspace-only compile on guest cc 8.3", "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": 12, "guest_dirty": false, "build_cmd": "cc -O2 -Wall -o loop_offbyone loop_offbyone.c", "run_cmd": "./loop_offbyone", "code_hash": "52e7ae6386cfa387f7a3403783d3efbbd74027545308fa6978ebc0a6e179633e", "notes": "Info-class correctness bug; Phase V (QEMU) verification skipped per contract - guest has no radeon GPU so the loop cannot execute on HW; the arithmetic proof harness WAS executed on the guest. Aggravation noted: usleep_range(100,200) maps to DELAY(100) (delay.h:55), a ~100us busy-wait per iteration held under chan->mutex (:96), ~100ms worst case per failed transaction vs upstream Linux udelay(1) ~1ms - bounded, latency-only. Error-path goto done skips the second DONE_ACK at :194; stale DONE is re-acked at :145 of the next call before GO, so cosmetic. Known pair DF-2146 (reply byte count, :177-191) and DF-2147 (aux_offset[instance], :44-63) NOT re-reported.", "recommended_fix": "Gate the timeout on the observed status word, not the iteration counter: replace if (retry_count >= 1000) with if (!(tmp & AUX_SW_DONE)) (see fix.diff, git-apply --check verified against pristine sys/)", "fix_status": "not_testable", "fix_baseline_reproduced": false, "fix_patched_reproduced": false, "fix_verdict": "fix.diff not boot-tested: validating it requires radeon DisplayPort hardware timing a completion into the final poll iteration, which the audit guest cannot provide (no AMD/ATI PCI device). The harness proves the status-keyed gate classifies every stimulus correctly (fixed column of run.log), including the case the shipped gate gets wrong; the diff is a one-line, behavior-preserving-for-all-other-paths change, git-apply --check verified against the pristine sys/ tree.", "fix_evidence": "run.log fixed gate column: 0/0/0/0/-5 vs shipped 0/0/0/-5/-5; fix.diff hunk @@ -157,7 +157,7 @@ replacing the counter test with !(tmp & AUX_SW_DONE)" } |