cik_sdma_soft_reset(): stale status variable β SDMA1-busy test reads mmSDMA0_F32_CNTL instead of mmSRBM_STATUS2, so a hung SDMA1 is never soft-reset
Summary
tmp holds the SRBM_STATUS2 snapshot; if the SDMA0 branch runs, tmp is reassigned to the F32_CNTL RMW value, and the following 'tmp & SDMA1_BUSY_MASK' tests an F32_CNTL value with a status mask. Whenever SDMA0 is busy (the typical both-engines-hung state), SDMA1 is never halted/reset - ring test fails, GPU unusable until reboot from an unpriv-triggerable illegal-instruction reset. Availability-only. Separate-status-variable fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4360 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| verdict.json | β | 2.5 KB | view raw | |
| manifest.json | β | 652 B | view raw |
DF-4360 β cik_sdma_soft_reset(): stale tmp makes the SDMA1-busy test read the wrong register β wedged engine survives reset
File: sys/dev/drm/amd/amdgpu/cik_sdma.c (pass 2)
Severity: Low Β· Confidence: certain
What (static proof)
sys/dev/drm/amd/amdgpu/cik_sdma.c:1089 u32 tmp = RREG32(mmSRBM_STATUS2);
sys/dev/drm/amd/amdgpu/cik_sdma.c:1091 if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK) {
sys/dev/drm/amd/amdgpu/cik_sdma.c:1093 tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET); /* tmp CLOBBERED */
sys/dev/drm/amd/amdgpu/cik_sdma.c:1094 tmp |= SDMA0_F32_CNTL__HALT_MASK;
sys/dev/drm/amd/amdgpu/cik_sdma.c:1095 WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
sys/dev/drm/amd/amdgpu/cik_sdma.c:1096 srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
sys/dev/drm/amd/amdgpu/cik_sdma.c:1097 }
sys/dev/drm/amd/amdgpu/cik_sdma.c:1098 if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK) { /* tests F32_CNTL, NOT SRBM_STATUS2 */
The second if is meant to test the SDMA1 busy bit of mmSRBM_STATUS2, but when the
SDMA0 branch executed, tmp now holds mmSDMA0_F32_CNTL | HALT. The
SRBM_STATUS2__SDMA1_BUSY_MASK bit(s) in that value are meaningless (normally clear),
so whenever SDMA0 is busy, SDMA1 is never halted/soft-reset, even if SDMA1 is also
wedged β precisely the both-engines-hung state a reset is supposed to clear.
(If the SDMA0 branch is NOT taken, tmp still holds SRBM_STATUS2 and the second test
is accidentally correct β which is why this survives casual reading.)
Reachability / impact
Callers: cik_sdma_resume() (cik_sdma.c:1051 β S3/S4 resume) and the device-reset path
armed by cik_sdma_process_illegal_inst_irq() β schedule_work(&adev->reset_work)
(cik_sdma.c:1217-1219), which an unprivileged DRI client can trigger by getting an
illegal instruction into an SDMA command stream while both engines have work queued.
Consequence of the missed reset: SDMA1 stays hung β amdgpu_ring_test_ring() fails in
cik_sdma_gfx_resume() (cik_sdma.c:502-508) β hw_init error β GPU unusable until
reboot. Availability-only, hardware-specific (CIK ASIC required) β Low / dos.
Fix
--- a/sys/dev/drm/amd/amdgpu/cik_sdma.c
+++ b/sys/dev/drm/amd/amdgpu/cik_sdma.c
@@ -1086,7 +1086,7 @@ static int cik_sdma_soft_reset(void *handle)
u32 srbm_soft_reset = 0;
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- u32 tmp = RREG32(mmSRBM_STATUS2);
+ u32 status = RREG32(mmSRBM_STATUS2), tmp;
- if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK) {
+ if (status & SRBM_STATUS2__SDMA_BUSY_MASK) {
/* sdma0 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
@@ -1098,7 +1098,7 @@ static int cik_sdma_soft_reset(void *handle)
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
}
- if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK) {
+ if (status & SRBM_STATUS2__SDMA1_BUSY_MASK) {
/* sdma1 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
Verification status
untested β requires CIK AMD hardware; the audit QEMU guest has no AMD GPU PCI device
(pciconf -l), so cik_sdma_soft_reset cannot execute there. Static proof above is
line-accurate; the stale-variable flow is unambiguous from source.
VERDICT β DF-4360
status: untested (hardware-gated) Β· impact (demonstrated): none; expected class: local DoS on CIK hardware Β· confidence: certain (stale-variable flow unambiguous).
Reproduced?
No β needs a CIK AMD GPU. Guest (DragonFly 6.5-DEVELOPMENT x86_64, QEMU/KVM) has no
0x1002 PCI device; cik_sdma_soft_reset cannot execute there. Static proof:
- cik_sdma.c:1089
tmp = RREG32(mmSRBM_STATUS2). - cik_sdma.c:1091-1097: if SDMA0 busy,
tmpis REASSIGNED toRREG32(mmSDMA0_F32_CNTL + SDMA0_OFFSET) | HALT. - cik_sdma.c:1098:
if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK)now tests an F32_CNTL value with the SDMA1 status mask β wrong register. In the both-engines-hung state the bit reads clear β SDMA1 never halted /SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASKnever set β soft reset leaves SDMA1 wedged. - Consumers: cik_sdma_resume (cik_sdma.c:1051) on S3/S4 resume, and the reset armed by cik_sdma_process_illegal_inst_irq β schedule_work(&adev->reset_work) (cik_sdma.c:1217-1219), reachable by an unprivileged DRI client that gets an illegal instruction into an SDMA stream. Result: amdgpu_ring_test_ring fails in cik_sdma_gfx_resume (cik_sdma.c:502-508) β hw_init error β GPU unusable until reboot.
Availability-only, hardware-specific β Low. No memory-corruption primitive (register
write targets are fixed MMIO offsets selected by constants, not by tmp).
Exploit chain
Unprivileged DRI user on CIK hardware: wedge SDMA0+SDMA1 (queued work + illegal SDMA opcode) β illegal-inst IRQ β reset_work β amdgpu device reset β cik_sdma_resume β soft_reset skips SDMA1 β engine stays hung β device reset fails. Ceiling: persistent GPU DoS. Not attempted on guest (no GPU).
Fix validation
not_testable β no CIK GPU to build/boot a patched kernel against. Fix keeps the status lvalue separate from the RMW scratch (see README diff); behavior for the non-SDMA0-busy path is bit-identical to today.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testablenot_testable β no CIK hardware on the audit guest to compare baseline vs patched kernel
fix diff in README.md
Confirmed kernel references
Detail
Exploit chain
(hardware-gated, not executed) unprivileged DRI client on CIK: queue work on both SDMA engines + inject illegal SDMA opcode -> illegal_inst IRQ -> schedule_work(reset_work) -> device reset -> cik_sdma_resume -> soft_reset skips SDMA1 (stale tmp) -> ring test fails -> device init error, GPU dead until reboot
Evidence (decisive lines)
README.md (annotated excerpt cik_sdma.c:1085-1104 + callers cik_sdma.c:502-508, 1047-1054, 1213-1220); VERDICT.md; guest pciconf shows no 0x1002 device
PoC changes
none β no PoC seed; static verification only (hardware-gated path)
Verified recommended fix
Keep the mmSRBM_STATUS2 snapshot in a separate variable (status) and test SDMA1_BUSY against it, not against the F32_CNTL RMW scratch
Verdict
cik_sdma_soft_reset() clobbers the mmSRBM_STATUS2 status variable with the mmSDMA0_F32_CNTL read inside the SDMA0 branch, so the following SDMA1_BUSY test examines the wrong register; whenever SDMA0 is busy the SDMA1 engine is never halted or soft-reset, leaving it wedged through resume and through the illegal-instruction reset path (cik_sdma_process_illegal_inst_irq -> reset_work) β a persistent local GPU denial of service on CIK hardware, availability-only. Runtime PoC impossible on the audit guest (no AMD PCI device); the stale-variable flow is unambiguous from source.
No comments yet.