evergreen_copy_dma discards radeon_sync_resv()/radeon_sync_rings() returns β Evergreen/Cayman twin of DF-4871/DF-5089: DMA copy committed without cross-ring ordering on sync failure, silent bo data corruption
Summary
The routed lead twin: both sync returns ignored inside the ring lock (resv waits also serialize every ring) - the copy commits unordered w.r.t. pending writes on degraded-driver states, silently corrupting migrated bo contents under a healthy fence. Check+move-resv-before-lock fix (mirror of the accepted DF-4871 fix), git-apply-verified. Hardware-gated (QEMU std-VGA proven this run).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5170 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 7.2 KB | β raw | |
| fix.diff | β | 1.1 KB | view raw | |
| env.txt | β | 436 B | view raw | |
| run.log | β | 439 B | view raw | |
| manifest.json | β | 1.5 KB | view raw | |
| verdict.json | β | 5.3 KB | view raw |
DF-5170 β evergreen_copy_dma discards radeon_sync_resv()/radeon_sync_rings() returns
The Evergreen/Cayman twin of DF-4871 (r600_dma.c:466-467) and DF-5089
(si_dma.c:255-256): evergreen_copy_dma() (evergreen_dma.c:105-158) takes
the DMA ring lock, then calls the sync layer and drops both int return
values, then emits and commits the copy packets unconditionally.
Reproduce
There is no userland PoC: the defect is reachable only on real
Evergreen/Cayman (AMD HD 5xxx/6xxx) hardware, as the ttm bo-migration copy
callback (.copy.copy = &evergreen_copy_dma, radeon_asic.c:1373/1467/1560/
1708/1828). The verification guest has QEMU std-VGA and no DRM stack β
see env.txt / run.log.
dfbsd-qemu/vm.sh status # guest up dfbsd-qemu/vm.sh run_root 'pciconf -l | grep -i vga; kldstat | grep -ci drm; ls /dev/dri'
Expected (the hardware gate): vgapci0 ... chip=0x11111234 (QEMU std VGA),
0, ls: /dev/dri: No such file or directory.
Source-level proof
See VERDICT.md for the full line-accurate trace (failure modes of
radeon_sync.c, the checked-and-unwind contrast in radeon_ib.c:149-154, and
the pass-2 negative-result table for the rest of evergreen_dma.c).
Fix
fix.diff β check both returns and unwind (unlock_undo + sync_free +
ERR_PTR), moving radeon_sync_resv() before radeon_ring_lock() because
its dma_fence_wait(..., true) may sleep. Mirrors the accepted DF-4871 fix
and DF-5089's fix.diff shape.
DF-5170 VERDICT β evergreen_copy_dma commits the DMA copy even when cross-ring sync fails
Status: untested (hardware-gated; no Evergreen/Cayman GPU on the
verification guest). Impact: silent GPU bo-content corruption on degraded
EG/Cayman hardware (I:L). Confidence: certain β the ignored int returns
are unambiguous line-level facts; the failure modes of the sync layer are
the same ones traced for DF-4871/DF-5089 and re-verified in this tree below.
1. The defect
evergreen_copy_dma() (sys/dev/drm/radeon/evergreen_dma.c:105-158) β the
ttm bo-migration copy callback for all Evergreen/Cayman asics
(.copy.dma/.copy.copy = &evergreen_copy_dma at radeon_asic.c:1373,
:1467, :1560, :1708, :1828; ring index R600_RING_TYPE_DMA_INDEX;
reached from radeon_move_bl()/radeon_ttm.c on every VRAM<->GTT move):
121: size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4;
122: num_loops = DIV_ROUND_UP(size_in_dw, 0xfffff);
123: r = radeon_ring_lock(rdev, ring, num_loops * 5 + 11);
124: if (r) { ... radeon_sync_free(...); return ERR_PTR(r); }
...
130: radeon_sync_resv(rdev, &sync, resv, false);
131: radeon_sync_rings(rdev, &sync, ring->idx);
132:
133: for (i = 0; i < num_loops; i++) { ...emit copy packets... }
...
147: r = radeon_fence_emit(rdev, &fence, ring->idx);
154: radeon_ring_unlock_commit(rdev, ring, false);
Both functions return int (radeon.h:628, radeon.h:632); both returns are
discarded at evergreen_dma.c:130-131. On failure the copy packets are still
emitted and committed at :154 β the exact twin of r600_dma.c:466-467
(DF-4871) and si_dma.c:255-256 (DF-5089). DF-5089's own verdict/notes flag
this line pair as the then-unfiled sibling; this filing closes it.
2. How the sync layer fails (verified 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). Note this happens while rdev->ring_lock is
already held (taken at evergreen_dma.c:123) β a sleep under the lock that
serializes every ring; the fix moves it before the lock.
radeon_sync_rings() (radeon_sync.c:137-201) β documented "The caller must
hold the ring lock" (:135):
- -EINVAL when a 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, exactly the degraded state a bo migration can 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 unwind (radeon_ring_unlock_undo
+ radeon_sync_free + ERR_PTR(r)) β the pattern the sibling caller
radeon_ib_schedule() implements for the very same call
(radeon_ib.c:149-154). Instead the 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 fence emitted at :147 only orders the
copy itself). Kernel memory is never corrupted; ceiling is unprivileged-
triggerable bo content corruption / potential GPU hang on degraded
hardware β Low, matching DF-4871/DF-5089.
3. Why runtime verification is not possible here
Guest probe (this run, run.log/env.txt): DragonFly dfbsd 6.5-DEVELOPMENT
... X86_64_GENERIC x86_64; VGA is vgapci0@pci0:0:2:0 ... chip=0x11111234
(QEMU std VGA); kldstat | grep -ci "radeon\|drm" = 0; /dev/dri absent.
evergreen_copy_dma is only reachable as the asic copy callback after the
radeon driver binds to real Evergreen/Cayman hardware; no deterministic
userland trigger exists without it. Hardware-gated Low severity, per
contract PoC-skippable; recorded untested (persist enum has no
not_testable/skipped). Attempts: 1 (env probe).
4. Pass-2 negative results (rest of evergreen_dma.c, with weight)
- DF-2167 known, not re-reported:
(num_gpu_pages << 12) / 4at :121 wraps 32-bit for num_gpu_pages >= 2^20 (>=4GB bo) β the EG sibling of DF-2091; already filed (Medium/memcorrupt). - Packet count field is exact:
DMA_PACKET(cmd, sub_cmd, n)places(n) & 0xFFFFFin a 20-bit field (evergreend.h:1416-1418); thecur_size_in_dw > 0xFFFFFclamp at :135-136 matches the field width exactly. No silent truncation. - No ring OOB write is possible:
radeon_ring_writemasks the write index withring->ptr_maskand only prints DRM_ERROR whencount_dw <= 0(radeon.h:2708-2718). The +11 slack at :123 covers the 7-dword fence packet (evergreen_dma_fence_ring_emit :45-54 writes 7); the semaphore- wait dwordsradeon_sync_ringsemits into this ring are NOT covered by the reservation (radeon_sync.c:186-187 "we assume caller has already allocated space on waiters ring" β ib_schedule reservesRADEON_NUM_SYNCS * 8for them, radeon_ib.c:135, but evergreen_copy_dma does not) β worst case is in-ring command corruption + DRM_ERROR spam on=2 concurrent sync waits, bounded by ptr_mask. No kernel memory safety.
- IB path reservation is ample:
evergreen_dma_ring_ib_executeemits <= 14 dwords (4 wb + <= 7 NOP pad + 3 IB, :75-88);radeon_ib_schedulelocks64 + RADEON_NUM_SYNCS * 8(radeon_ib.c:135).ib->length_dwis bounded by the SA-manager IB pool (radeon_ib.c:201/210, RADEON_IB_POOL_SIZE*64KB) far below 2^20, so(ib->length_dw << 12)at :88 cannot alias other packet fields. - 64-bit offsets do not wrap in u32 arithmetic: per-iteration
src_offset += cur_size_in_dw * 4(:143-144) with cur_size_in_dw <= 0xFFFFF keeps the product <= 0x3FFFFC in u32 before promotion;upper_32_bits(...) & 0xff(:141-142) matches the 40-bit GPU VA space. - Reservation size is sanity-checked: even with DF-2167's wrapped
size_in_dw, num_loops <= ceil(0xFFFFFFFF/0xFFFFF) = 1025, so
num_loops * 5 + 11<= 5136 dwords;radeon_ring_allocrejectsndw > ring->ring_size / 4with -ENOMEM (radeon_ring.c:112-113). - wb-enabled rptr tracking is self-consistent (:71-78): next_rptr computed from the same alignment invariant the padding loop enforces (:84-85); u32 wptr with ptr_mask wrap.
- Lockup checker (:169-177) is read-only.
- No fill/linear/broadcast op exists in this file (that is cik_sdma); the file exports only fence/ib/copy/lockup.
5. Fix validation
fix.diff authored from line-accurate source reading, mirroring the
accepted DF-4871 fix and DF-5089's fix.diff: check radeon_sync_resv()
before radeon_ring_lock() (its dma_fence_wait may sleep), check
radeon_sync_rings() after the lock and unwind on failure. Kernel
fix-build validation impossible on this guest β no Evergreen/Cayman
hardware to drive the DMA migration path; fix_status=not_testable.
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 and verified to apply cleanly against sys/dev/drm/radeon/evergreen_dma.c (git apply --check, sys/ untouched); mirrors the accepted DF-4871 fix shape and the checked sibling (radeon_ib.c:149-154). Kernel fix-build validation impossible on this guest β no Evergreen/Cayman hardware to drive the DMA migration path.
findings/poc/DF-5170/fix.diff
Confirmed kernel references
- sys/dev/drm/radeon/evergreen_dma.c:130
- sys/dev/drm/radeon/evergreen_dma.c:131
- sys/dev/drm/radeon/evergreen_dma.c:154
- sys/dev/drm/radeon/radeon_sync.c:106
- sys/dev/drm/radeon/radeon_sync.c:153
- sys/dev/drm/radeon/radeon_sync.c:165
- sys/dev/drm/radeon/radeon_sync.c:172
- sys/dev/drm/radeon/radeon_ib.c:135
- sys/dev/drm/radeon/radeon_ib.c:149
- sys/dev/drm/radeon/radeon.h:628
- sys/dev/drm/radeon/radeon.h:2708
- sys/dev/drm/radeon/evergreend.h:1416
- sys/dev/drm/radeon/radeon_asic.c:1373
Detail
Evidence (decisive lines)
findings/poc/DF-5170/VERDICT.md (trace: radeon_ttm.c radeon_move_bl -> evergreen_dma.c:121-154 -> radeon_sync.c:89-199 failure modes -> unchecked commit at :154; contrast radeon_ib.c:135,149-154; plus the full pass-2 negative-result table for the rest of evergreen_dma.c incl. ptr_mask-bounded ring writes radeon.h:2708-2718 and the exact 20-bit DMA_PACKET count field evergreend.h:1416-1418); env.txt + run.log (hardware gate probe, this run); fix.diff (git-apply-verified against sys/, mirrors accepted DF-4871 / DF-5089 fix shape)
PoC changes
n/a β no PoC seed; no deterministic userland trigger without Evergreen/Cayman-class hardware in a degraded state
Verified recommended fix
Check radeon_sync_resv()/radeon_sync_rings() returns in evergreen_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
evergreen_copy_dma() discards the return values of radeon_sync_resv() (evergreen_dma.c:130) and radeon_sync_rings() (evergreen_dma.c:131) β the Evergreen/Cayman twin of DF-4871 (r600_dma.c:466-467) and DF-5089 (si_dma.c:255-256), and the exact site DF-5089's notes flagged as unfiled. 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) β currently called while already holding rdev->ring_lock (taken at evergreen_dma.c:123), so that sleep also serializes every ring. On failure the copy packets are still emitted and committed (evergreen_dma.c:133-145, :154), 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 fence at :147 only orders the copy itself). The checked-and-unwind pattern exists in the sibling caller radeon_ib_schedule() for the same call (radeon_ib.c:149-154). Runtime verification is hardware-gated (needs real Evergreen/Cayman hardware in a degraded state; guest is QEMU std-VGA chip=0x11111234, 0 drm modules, no /dev/dri β env.txt/run.log); hardware-gated Low, per contract PoC-skippable, recorded 'untested' (persist enum has no not_testable/skipped). Impact ceiling: unprivileged-triggerable bo content corruption on degraded hardware (I:L); no kernel memory corruption.
No comments yet.