DF-4871 family completion: seven more callers discard radeon_sync_resv()/radeon_sync_rings() returns β GPU work submitted without its cross-ring dependency on sync failure (silent bo data corruption, racing VM page-table updates)
Summary
Mechanical enumeration of every caller found 7 more unfiled bare-call sites (r600.c/cik.c/evergreen_dma.c/rv770_dma.c/cik_sdma.c copy paths + radeon_vm.c:705/:833 where r is additionally clobbered) - on -ERESTARTSYS/-ENOMEM/ring-alloc failure the copy or PT-update IB is still committed without the dependency. check_static.sh classifies all 11 sites (7 new + 2 known + 2 correct). 7-site check-and-rollback fix (git apply clean). Hardware-gated for runtime.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5131 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.8 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| check_static.sh | β | 1.7 KB | view raw | |
| check_static.log | β | 1.7 KB | view raw | |
| fix.diff | β | 4.6 KB | view raw | |
| manifest.json | β | 1.4 KB | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-5131 β radeon_sync_resv()/radeon_sync_rings() returns discarded by 7 remaining callers (DF-4871 family completion)
File: sys/dev/drm/radeon/radeon_sync.c (pass 2) β findings live at the call sites
enumerated below; the sync engine (radeon_sync.c:89-201) correctly returns the
error, the callers drop it.
What this is
radeon_sync_resv() (radeon_sync.c:89-125) and radeon_sync_rings()
(radeon_sync.c:137-201) return int precisely so a submitter can abort when a
cross-ring dependency cannot be established (interrupted fence wait
-ERESTARTSYS, semaphore SA-bo allocation -ENOMEM, radeon_ring_alloc()
fence-wait failure, disabled source ring -EINVAL). Pass 1 already filed two
of the offenders:
- DF-4871 β
sys/dev/drm/radeon/r600_dma.c:466-467(r600_copy_dma) - DF-5089 β
sys/dev/drm/radeon/si_dma.c:255-256(si_copy_dma)
This pass found seven more call sites that discard the return, none covered
by an existing finding (checked against findings for cik_sdma.c, r600.c,
rv770_dma.c, evergreen_dma.c, cik.c, radeon_vm.c):
| # | Site | Function | What is dropped |
|---|------|---------------------------|
| 1 | sys/dev/drm/radeon/r600.c:2997-2998 | r600_copy_blit | resv + rings |
| 2 | sys/dev/drm/radeon/cik.c:3695-3696 | cik_copy_blit | resv + rings |
| 3 | sys/dev/drm/radeon/evergreen_dma.c:130-131 | evergreen_copy_dma | resv + rings |
| 4 | sys/dev/drm/radeon/rv770_dma.c:66-67 | rv770_copy_dma | resv + rings |
| 5 | sys/dev/drm/radeon/cik_sdma.c:601-602 | cik_copy_dma | resv + rings |
| 6 | sys/dev/drm/radeon/radeon_vm.c:705 | radeon_vm_update_page_tables | resv (PD fence) |
| 7 | sys/dev/drm/radeon/radeon_vm.c:833 | radeon_vm_update_ptes | resv β and r is immediately clobbered by the following reservation_object_reserve_shared() call, so the failure could never be observed even by accident |
All five copy sites hold rdev->ring_lock at the point of the call (so the
sync engine's cross-ring radeon_ring_alloc() is serialized β not a locking
bug), and all proceed to emit the copy/PT-update commands and commit the ring
after the unchecked sync failure.
Impact
On any sync failure the DMA copy / blit / page-table update is submitted without the cross-ring dependency it exists to enforce:
- copy paths: silent stale-data bo corruption (the GPU reads the destination before the signaling ring finished writing it) β integrity impact;
- VM paths (6,7): page-table updates racing the fences on the PD/PT resv β GPU-visible memory-consistency violation, i.e. the GPU can walk page tables mid-update;
- the
-ENOMEM/ disabled-ring cases then also leak nothing (sync_free is still called on the success tail), but the ordering guarantee is silently gone.
Severity Low (family precedent DF-4871/DF-5089): hardware-gated to radeon GPUs, needs a sync failure to coincide with a cross-ring dependency β but the code path is certain, static, and trivially fixed.
Reproduce (static)
./check_static.sh
enumerates every radeon_sync_resv/radeon_sync_rings call in
sys/dev/drm/radeon/ and classifies checked vs bare β expect exactly the
7 sites above reported BARE (plus the 2 known DF-4871/DF-5089 sites), and
radeon_ib.c:149 / radeon_cs.c:256 reported CHECKED.
Runtime reproduction requires a physical radeon GPU with β₯2 initialized rings
plus an induced radeon_sync_rings() failure (e.g. signal-pending
dma_fence_wait); the QEMU audit guest has no AMD GPU (pciconf shows none),
so no runtime PoC is possible on it β see VERDICT.md.
Fix
fix.diff β check both returns at all 7 sites with the canonical rollback
(radeon_ring_unlock_undo() + radeon_sync_free() + ERR_PTR(r) for the
copy paths; propagate r for the two VM sites, un-clobbering the variable at
radeon_vm.c:833).
DF-5131 β VERDICT
status: untested (statically certain / runtime not testable on audit guest) reproduced: 0 Β· impact: none (demonstrable only as silent data-ordering loss on real radeon HW)
What was verified, and how
This is a pure static-contract defect: seven call sites invoke
radeon_sync_resv() / radeon_sync_rings() as bare statements and proceed to
emit + commit GPU work regardless of the returned error. The engine side
(sys/dev/drm/radeon/radeon_sync.c:89-125, 137-201) is correct:
radeon_sync_resvreturns thedma_fence_wait()result on both the exclusive (radeon_sync.c:106) and shared (radeon_sync.c:119-122) paths;radeon_sync_ringsreturns-EINVAL(disabled source ring, radeon_sync.c:153-156), theradeon_fence_wait()failure (radeon_sync.c:160-163, 180-183, 190-193),radeon_semaphore_create()failure (radeon_sync.c:165-167) andradeon_ring_alloc()failure (radeon_sync.c:172-174).
The callers drop it. check_static.sh (this pack, run against the read-only
sys/ tree, output in check_static.log) mechanically confirms:
- BARE (defective): r600.c:2997, cik.c:3695, evergreen_dma.c:130, rv770_dma.c:66, cik_sdma.c:601, radeon_vm.c:705, radeon_vm.c:833 (+ the already-filed r600_dma.c:466 / si_dma.c:255 = DF-4871/DF-5089);
- CHECKED (correct): radeon_ib.c:149 (
r = radeon_sync_rings(...)with rollback), radeon_cs.c:256 (r = radeon_sync_resv(...)propagated).
Why no runtime PoC
- The QEMU audit guest (DragonFly 6.5-DEVELOPMENT x86_64, uname in verdict.json)
has no AMD/ATI PCI device (
pciconf -l | grep -ic 'ati|amd|1002'β 0), so neitherradeonnoramdgpucan attach; the code cannot execute. - Even on hardware, the observable is silent loss of GPU execution ordering
(a stale-data race), not a crash: triggering needs a sync failure
(
-ERESTARTSYSon a signal-pending fence wait,-ENOMEMinradeon_semaphore_create, or aradeon_ring_alloc()fence-drain failure) to coincide with a cross-ring dependency on a bo the process can observe. That is a real but probabilistic corruption window β exactly why DF-4871 and DF-5089 were also filed Low on static certainty.
Per the pass-2 contract this is a Low finding outside the mandatory-verify buckets (not hammer2/memcorrupt/privesc, not Critical/High, not a trivially runnable local program) β verification honestly recorded as untested, with the machine-checkable static proof attached instead of a fabricated runtime claim.
Exploit chain
(none β GPU-internal ordering loss; worst case silent bo data corruption or a racing page-table update on radeon hardware)
Fix validation
Not applicable (no runtime baseline exists without hardware). fix.diff was
authored after the static verification above: identical rollback pattern to the
one radeon_ib_schedule() already uses for the same call
(sys/dev/drm/radeon/radeon_ib.c:149-155). It applies cleanly to the unmodified
tree (git apply --check verified) but was intentionally not applied to
sys/.
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.
Fix verification
not_testablefix validation not applicable: no runtime baseline can exist on the QEMU audit guest (no AMD GPU) and the defect's observable is a probabilistic hardware ordering race; fix.diff was verified to apply cleanly (git apply --check) to the unmodified tree and mirrors the already-correct radeon_ib.c:149-155 pattern
git apply --check findings/poc/DF-5131/fix.diff => clean; diff hunks at cik.c @@ -3692,8 +3692,19 @@, radeon_vm.c @@ -702,7 +702,11 @@ and @@ -830,7 +834,9 @@
Confirmed kernel references
- sys/dev/drm/radeon/radeon_sync.c:89-125
- sys/dev/drm/radeon/radeon_sync.c:137-201
- sys/dev/drm/radeon/r600.c:2997-2998
- sys/dev/drm/radeon/cik.c:3695-3696
- sys/dev/drm/radeon/evergreen_dma.c:130-131
- sys/dev/drm/radeon/rv770_dma.c:66-67
- sys/dev/drm/radeon/cik_sdma.c:601-602
- sys/dev/drm/radeon/radeon_vm.c:705
- sys/dev/drm/radeon/radeon_vm.c:833
Detail
Evidence (decisive lines)
check_static.log lines 'BARE cik.c:3695' through 'BARE radeon_vm.c:833' (7 unfiled sites), 'CHECKED radeon_ib.c:149' / 'CHECKED radeon_cs.c:256' (correct pattern), and fix.diff hunks @@ -3692,8 +3692,19 @@ cik.c / @@ -702,7 +702,11 @@ radeon_vm.c showing the canonical rollback fix; VERDICT.md for the full trace incl. the radeon_vm.c:833 r-clobber detail
PoC changes
no seed PoC existed (filed during pass 2 with untested verdict); authored check_static.sh as the machine-checkable proof since no runtime trigger is possible without radeon hardware
Verified recommended fix
Check both sync returns at all 7 sites with the radeon_ib_schedule-style rollback (radeon_ring_unlock_undo + radeon_sync_free + ERR_PTR for copy paths; propagate r at the two radeon_vm.c sites, un-clobbering r at :833) - see fix.diff
Verdict
Static contract defect, mechanically certain: seven call sites (r600.c:2997-2998, cik.c:3695-3696, evergreen_dma.c:130-131, rv770_dma.c:66-67, cik_sdma.c:601-602, radeon_vm.c:705, radeon_vm.c:833) invoke radeon_sync_resv()/radeon_sync_rings() as bare statements and submit + commit GPU work regardless of failure, silently dropping the cross-ring ordering guarantee (silent bo data corruption / racing page-table updates on radeon hardware). Runtime reproduction is impossible on the audit guest (no AMD GPU: pciconf shows zero 1002: devices) and on hardware the observable is a probabilistic stale-data race, matching the Low/hardware-gated family precedent (DF-4871, DF-5089). Verification therefore honestly recorded as untested; the machine-checkable static proof (check_static.sh -> check_static.log: exactly the 7 predicted unfiled bare sites, both known sites, and both correct sites) stands in for a runtime PoC.
No comments yet.