si_copy_dma discards radeon_sync_resv()/radeon_sync_rings() returns β SI twin of DF-4871: DMA copy committed without cross-ring ordering on sync failure, silent bo data corruption
Summary
Both sync returns ignored; on ring-not-ready (failed ib test / GPU reset) or semaphore/alloc failure the copy still commits unordered w.r.t. pending writes - migrated bo holds stale/partial data under a healthy fence. Sibling radeon_ib_schedule checks the identical calls and unwinds. Check+move-resv-before-lock fix (mirror of the accepted DF-4871 fix). Hardware-gated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5089 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 7.2 KB | β raw | |
| fix.diff | β | 1.1 KB | view raw | |
| env.txt | β | 456 B | view raw | |
| run.log | β | 285 B | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-5089 β si_copy_dma discards radeon_sync_resv()/radeon_sync_rings() returns (SI twin of DF-4871)
File: sys/dev/drm/radeon/si_dma.c (pass 2)
What
si_copy_dma() calls both sync functions and ignores that both return int
(si_dma.c:255-256). On any sync failure the DMA copy packets are still emitted
and the ring committed (:258-279), so the GPU-side copy runs unordered with
respect to a pending write on the same bo β silent corruption of the migrated
buffer's contents, with a healthy-looking fence on top.
This is the SI twin of DF-4871 (r600_dma.c:466-467, same pass-2 batch family) and was explicitly flagged in the r600 pass-2 task_log ("identical ignored radeon_sync_resv/rings returns at 255-256 β check in own-file audits"). DF-4871's verdict already cross-referenced this exact line pair.
Failure modes that make the ignored return real (radeon_sync.c):
- radeon_sync_rings() β -EINVAL "Syncing to a disabled ring"
(radeon_sync.c:153-156) β reachable when a source ring went not-ready
(failed ib test / GPU reset path).
- interrupted manual radeon_fence_wait() fallback (:160-163) and
radeon_semaphore_create() SA-allocator failure (:165-167) on
memory pressure.
- radeon_sync_resv() β interrupted dma_fence_wait(f, true) on a foreign
(non-radeon) resv fence (radeon_sync.c:106, 119).
Contrast: the sibling caller radeon_ib_schedule() checks the identical call
and unwinds with radeon_ring_unlock_undo() (radeon_ib.c:149-154).
Reproduce
Runtime trigger requires real SI (Tahiti/Pitcairn/Verde/Oland/Hainan/
Sea Islands-PCIe) hardware in a degraded state β the audit guest has none
(QEMU std VGA, 0 drm modules, no /dev/dri β env.txt). Hardware-gated Low
finding, per contract PoC-skippable; recorded untested. Line-level trace
in VERDICT.md.
Fix
See fix.diff β checks both returns, moves radeon_sync_resv() before the
ring lock (its waits may sleep and must not hold rdev->ring_lock), unwinds
with radeon_ring_unlock_undo() + radeon_sync_free() + ERR_PTR(r)
on radeon_sync_rings() failure. Mirrors the DF-4871 fix shape.
DF-5089 VERDICT β si_copy_dma commits the DMA copy even when cross-ring sync fails
Status: untested (hardware-gated; no SI GPU on the verification guest).
Impact: silent GPU bo-content corruption on degraded SI hardware (I:L).
Confidence: certain β the ignored int returns are unambiguous line-level
facts; the failure modes of the sync layer are traced below.
1. The defect
si_copy_dma() (si_dma.c:231-283) β the ttm bo-migration copy callback for
all SI asics (radeon_asic.c si_asic: .copy.dma = &si_copy_dma,
.copy.dma_ring_index = R600_RING_TYPE_DMA_INDEX; reached from
radeon_move_bl()/radeon_ttm.c on every VRAMβGTT move):
248: r = radeon_ring_lock(rdev, ring, num_loops * 5 + 11);
249: if (r) { ... return ERR_PTR(r); }
...
255: radeon_sync_resv(rdev, &sync, resv, false);
256: radeon_sync_rings(rdev, &sync, ring->idx);
257:
258: for (i = 0; i < num_loops; i++) { ...emit copy packets... }
...
272: r = radeon_fence_emit(rdev, &fence, ring->idx);
279: radeon_ring_unlock_commit(rdev, ring, false);
Both functions return int (radeon.h:628, radeon.h:632) and both returns are
discarded. On failure the copy is emitted and committed anyway.
2. How the sync layer fails (all paths in this tree)
radeon_sync_resv() (radeon_sync.c:89-125): returns the error from
dma_fence_wait(f, true) on any non-radeon exclusive/shared resv fence
(:106, :119 β interruptible wait, e.g. signal during a long foreign-fence
wait under memory pressure).
radeon_sync_rings() (radeon_sync.c:137-199) β documented "The caller must
hold the ring lock" (:135):
- -EINVAL when the fence's source ring is not ready (:153-156,
"Syncing to a disabled ring!") β a ring becomes !ready after a failed
ib test or GPU reset handling (radeon_ib.c sets ring->ready = false
on ib-test failure), which is exactly the degraded state a bo migration
can then race with;
- error from the manual radeon_fence_wait() fallback when
RADEON_NUM_SYNCS is exhausted (:158-163);
- error from radeon_semaphore_create() (SA allocator, -ENOMEM) (:165-167)
or radeon_ring_alloc() on the source ring (:172-174);
- error from the manual wait when semaphore emission failed (:177-193).
On any of these, the correct behavior is to abort the copy (ttm falls back to
memcpy in radeon_move_bl()); instead the copy is committed unordered β
the destination bo can hold stale/partial data while carrying a good fence,
i.e. silent data corruption of migrated GEM contents. This is the exact
defect class already accepted as DF-4871 for r600_copy_dma()
(r600_dma.c:466-467, Low); DF-4871's verdict cross-referenced si_dma.c:255-256
as an open sibling. Same class, same severity calibration:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N, CWE-754.
The check-and-unwind pattern this file should use exists 90 lines away in the
same subsystem: radeon_ib_schedule() treats the identical
radeon_sync_rings() failure as fatal (radeon_ib.c:149-154:
radeon_ring_unlock_undo(rdev, ring); return r;).
3. Why not higher severity / why not memory-safety
- The unordered window corrupts the bo's own contents, not kernel memory; there is no length/pointer consequence β the ring writes stay within the locked, mask-bounded circular ring.
- Trigger requires SI hardware in a degraded state (disabled source ring, allocation failure, or interrupted foreign-fence wait) coinciding with a bo migration β hence AC:H.
4. Pass-2 negative notes for si_dma.c (classes hunted and killed)
- DF-2091 (known, not re-reported):
size_in_bytes = (num_gpu_pages << 12)u32 truncation for β₯4GB moves (:246);num_loopsderives from the truncated value (:247). - fill sizes: no fill function exists in si_dma.c (DMA_PACKET_CONSTANT_FILL appears only in r600/evergreen CS validators) β nothing to audit.
- vm_*_pages integer math:
count*8(:75) would overflow only at count β₯ 2^29; callers bound nptes β€ ~3.07e8 via thendw > 0xfffff β -ENOMEMgate in radeon_vm_bo_update (radeon_vm.c:997-998), and PDE counts are β€ 2^22 β no overflow. Caps 0xFFFF8/0xFFFFE fit the 20-bit DMA_PACKET count fields (sid.h:1853-1866); chunk decrements are exact multiples (0xFFFF8 = 8Β·131071; ndw always even) β loops terminate with exact consumption. - IB overflow via emitters:
ib->ptr[ib->length_dw++]writes are unbounded in the emitters but every caller's estimate dominates the emission: copy 5 dws/chunk vs estimate 7/cmd; write 3+2/PTE vs 4/cmd + 2Β·nptes; set 9/chunk vs 10/cmd + 2Β·10 + 64 padding (radeon_vm.c:971-994, wrapper dispatch radeon_vm.c:359-379; fragment splits add β€2 boundary calls, covered by padding; full-block PTE ranges are fragment-aligned because PT BOs are β₯4KB-aligned). PDE update path: count<3 breaks emit β€7 dws vs 6/PDE estimate (radeon_vm.c:653-656 vs wrapper write path 3+2Β·count). WARN_ON tripwires exist at radeon_vm.c:706/1021. - ring space / OOB write:
radeon_ring_alloc()guarantees ndw < actual free dwords (radeon_ring.c:125-137 waits on fences until space exists) andradeon_ring_write()masks wptr into the circular buffer (radeon.h:2708) β over-commit (e.g. β₯2 semaphore waits vs the 4-dw slack after the 7-dw fence innum_loops*5 + 11) only trips the DRM_ERROR at radeon.h:2710;ring_free_dwis decremented per actual write so later allocs stay truthful. Upstream-inherited accounting wart, not memory corruption. - vm flush indexes:
vm_idcomes fromradeon_vm_grab_id()(radeon_vm.c:177-222) bounded to [1, nvm-1],nvm = 16for SI (si.c:4787), so both register branches (:192-196) address existing VM_CONTEXT0-15 registers and1 << vm_id(:207, :213) is defined (β€ 1<<15).radeon_vm_flush()passes exactly that id (radeon_vm.c:250-251). - fence pairs / error paths: fence-emit failure unwinds with
radeon_ring_unlock_undo+radeon_sync_free(NULL)(:272-277) β the sync->semaphores[] array is released byradeon_sync_free, no leak (this file has no ib_test β SI DMA rings reuser600_dma_ib_test, radeon_asic.c:1901-1915, so the DF-4870 leak twin belongs to r600_dma.c, already filed). - Sibling callers with the same ignored returns (NOT this file, for the orchestrator's cross-refs): evergreen_dma.c:130-131, cik.c:3695-3696.
5. Why untested (honest status)
Runtime verification needs SI-class Radeon hardware driving the ttm
migration path while a sync dependency fails; the verification guest is
QEMU std-VGA-only (env.txt: chip=0x11111234, 0 drm modules, no /dev/dri).
Hardware-gated Low finding β per contract PoC-skippable β recorded
untested (the persist enum has no not_testable/skipped member).
6. Fix
fix.diff mirrors the accepted DF-4871 fix shape: check
radeon_sync_resv() before taking the ring lock (its waits may sleep and
must not hold rdev->ring_lock), check radeon_sync_rings() after the lock
and unwind with radeon_ring_unlock_undo() + radeon_sync_free() +
ERR_PTR(r).
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.diff authored from line-accurate source reading, mirroring the accepted DF-4871 fix shape and the checked sibling (radeon_ib.c:149-154); kernel fix-build validation impossible on this guest β no SI hardware to drive the DMA migration path
findings/poc/DF-5089/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-5089/VERDICT.md (trace: radeon_ttm.c radeon_move_bl -> si_dma.c:244-279 -> radeon_sync.c:89-199 failure modes -> unchecked commit at :279; contrast radeon_ib.c:149-154; plus full pass-2 negative-result table for the rest of si_dma.c); env.txt (hardware gate); fix.diff (mirrors accepted DF-4871 fix shape)
PoC changes
n/a β no PoC seed; no deterministic userland trigger without SI-class hardware in a degraded state
Verified recommended fix
Check radeon_sync_resv()/radeon_sync_rings() returns in si_copy_dma() and unwind (unlock_undo + sync_free + ERR_PTR) on failure, moving sync_resv before radeon_ring_lock() β mirror of the accepted DF-4871 fix
Verdict
si_copy_dma() discards the return values of radeon_sync_resv() (si_dma.c:255) and radeon_sync_rings() (si_dma.c:256) β the SI twin of DF-4871 (r600_dma.c:466-467), cross-referenced by DF-4871's own verdict and the r600 pass-2 task_log. radeon_sync_rings() fails with -EINVAL when a source ring was disabled (radeon_sync.c:153-156, reachable after a failed ib test / GPU reset) and on semaphore-create/ring-alloc/interrupted-manual-wait errors (:158-193); radeon_sync_resv() propagates interrupted dma_fence_wait() on foreign resv fences (radeon_sync.c:106,119). On failure the copy packets are still emitted and committed (si_dma.c:258-279), so the DMA copy runs unordered w.r.t. a pending write on the same bo β silent GPU-side corruption of the migrated bo's contents under a healthy fence. The checked-and-unwind pattern exists in the sibling caller radeon_ib_schedule() (radeon_ib.c:149-154). Runtime verification is hardware-gated (needs real SI hardware in a degraded state; guest is QEMU std-VGA, 0 drm modules, no /dev/dri β env.txt); hardware-gated Low, per contract PoC-skippable, recorded 'untested' (persist enum has no not_testable). Impact ceiling: unprivileged-triggerable bo content corruption on degraded hardware (I:L); no kernel memory corruption.
No comments yet.