β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4843

Unvalidated device-DMA'd IH wptr (alignment never checked, only masked) + unbounded amdgpu_ih_process ring walk β€” a misaligned wptr can never equal the 16-byte-stepping rptr, permanently hard-spinning the amdgpu interrupt thread (all GPU interrupt delivery dead)

Summary

get_wptr consumes the GPU-writeback u32 with nothing but &ptr_mask; the process loop terminates on rptr==wptr with rptr stepping 16 bytes - a wptr not a multiple of 16 (hostile fw DMA, corrupted writeback after PCIe fault) can never equal rptr: the ithread laps the 64KiB ring forever re-dispatching 4096 entries/lap with ih->lock held, one CPU pegged in interrupt context and all GPU interrupt delivery dead. No entry-count cap anywhere (genuine storms wedge equally). Validate-alignment + cap-per-invocation fix. Hostile-device model; hw-gated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4843 Β· 3 files
FileTypeDescriptionSize
README.md β€” 2.1 KB ↓ raw
trigger_sketch.c β€” 1.8 KB view raw
verdict.json β€” 2.0 KB view raw

DF-4843 β€” unvalidated IH wptr + unbounded ring walk = permanent interrupt-thread livelock

sys/dev/drm/amd/amdgpu/tonga_ih.c + amdgpu_ih.c.

The VI IH writeback slot (adev->irq.ih.ring[wptr_offs], a device-DMA'd u32) is consumed by tonga_ih_get_wptr() (tonga_ih.c:196-219) with nothing but a ptr_mask β€” no alignment validation, no sanity bound. amdgpu_ih_process() (amdgpu_ih.c:131-168) then walks while (ih->rptr != wptr) with rptr advancing only in 16-byte IV steps (tonga_ih.c:245/277) and re-reads wptr in restart_ih (amdgpu_ih.c:163-165).

A wptr that is not a multiple of 16 β€” arbitrary in the hostile-device model (compromised GPU/firmware DMA), plausible in the faulting-device model (garbage writeback after a PCIe error) β€” can never equal rptr: the walk laps the 64 KiB ring forever, re-dispatching 4096 entries per lap through the full amdgpu_irq_dispatch() chain (MMIO-heavy handlers), with ih->lock held. Result: permanent hard-spin of the amdgpu ithread β€” one CPU pegged in interrupt context, all GPU interrupt delivery dead for the life of the box. No entry-count cap exists anywhere on the path (an interrupt storm from a livelier hostile device keeps it equally busy even with an aligned wptr).

Status: UNTESTED

Requires a Tonga/Vega10 device (or an adev fake whose set_rptr MMIO is stubbed) to drive the real loop; the audit guest has no GPU. The code path is certain by inspection (see trigger_sketch.c for the exact loop-lap argument). DoS only β€” no memory-safety impact found on this file's surface: entry reads are bounded by rptr & ptr_mask + 4-dword entries inside the 64 KiB + 8 allocation, src_id/client_id are bounds-checked in dispatch (amdgpu_irq.c:409-417), and the NULL .process sibling issue is DF-4713.

Suggested fix

Validate wptr alignment/relationship in the amdgpu_ih_get_wptr implementations (tonga_ih.c:218: reject/align wptr & ~0xf mismatch with a dev_err + ring reset), and cap the per-invocation entry count in amdgpu_ih_process() (amdgpu_ih.c:154) so a storm or a bad wptr yields to the scheduler instead of hard-spinning the ithread.

Fix verification

not_testable
per-fix-DF-4843

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md (loop-lap argument with path:line); trigger_sketch.c

PoC changes

sketch only; no runnable PoC without hardware

Verified recommended fix

validate wptr alignment in tonga_ih_get_wptr (reject/repair wptr & ~0xf mismatch with a ring reset) and cap per-invocation entry count in amdgpu_ih_process so a storm or bad wptr cannot hard-spin the ithread

Verdict

Not run on the audit guest (requires a Tonga/Vega10 GPU or an adev fake with stubbed set_rptr MMIO; the guest has no GPU). Code-certain by inspection: tonga_ih_get_wptr (tonga_ih.c:196-219) returns the device-DMA'd writeback wptr with only a ptr_mask - no alignment check - and amdgpu_ih_process (amdgpu_ih.c:154-165) walks the ring with rptr advancing strictly in 16-byte steps and restart_ih re-reading the same wptr. A wptr not a multiple of 16 can never equal rptr, so the ithread laps the 64KiB ring forever re-dispatching entries with ih->lock held: permanent interrupt-thread hard-spin (one CPU pegged, all GPU interrupts dead). Hostile/faulting-device DoS only; no memory-safety issue on this path (entry reads masked+bounded, src_id/client_id checked in dispatch).