cik_ih: unvalidated device-DMA'd IH wptr β DF-4843/DF-4854 twin on the 16-byte CIK grid: a misaligned wptr permanently hard-spins the amdgpu interrupt thread, and the RB_OVERFLOW recovery preserves the bad phase so wptrβ[0xFFE4..0xFFEF]|OVERFLOW parks rptr at [0xFFF4..0xFFFF] β up to 12-byte OOB read past the zero-slack 64 KiB GTT ring BO
Summary
Same shape as the tonga/vega10 twins (same wb wptr source, +16 recovery): a non-16-multiple wptr can never equal the 16-byte-stepping rptr - the ithread laps the ring forever with ih->lock held (all GPU IRQs dead); the RB_OVERFLOW recovery imports the bad 4-granular phase so the first decode reads up to 12 bytes past the EXACT-SIZE GTT ring BO (CIK has no +8 slack). Hostile/faulting-device model; no AMD GPU on guest. Align-normalize + cap-the-walk fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4887 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 4.1 KB | β raw | |
| VERDICT.md | β | 4.7 KB | β raw | |
| trigger_sketch.c | β | 1.7 KB | view raw | |
| fix.diff | β | 2.4 KB | view raw | |
| verdict.json | β | 4.8 KB | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| env.txt | β | 333 B | view raw |
DF-4887 β cik_ih: unvalidated device-DMA'd IH wptr (DF-4843 twin, 16-byte grid) + phase-preserving RB_OVERFLOW recovery parking rptr within 12 bytes of ring end β up to 12-byte OOB read past the 64 KiB GTT ring BO
File: sys/dev/drm/amd/amdgpu/cik_ih.c:186-206 (with the shared walk at
amdgpu_ih.c:140-165).
Answers to the batch's specific cik questions
- wptr source: cik DOES use the writeback buffer, same as tonga β
wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs])(cik_ih.c:190);cik_ih_sw_initpassesuse_bus_addr=false(cik_ih.c:321) so thering[]-embedded shadow path never runs. NOT a different shape: this is the DF-4843 shape on a 16-byte IV grid. - doorbell: none β CIK predates IH doorbells. The funcs table
(cik_ih.c:462-467) has only get_wptr/prescreen_iv/decode_iv/set_rptr,
and
set_rptruses themmIH_RB_RPTRregister (cik_ih.c:297-300). - RB_OVERFLOW path: present and same-phase as tonga:
adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask(cik_ih.c:200) β the DF-4854 aggravator exists here too (12-byte variant, worse in one respect: the GTT BO allocation has NO +8 slack).
The defect
cik_ih_get_wptr consumes the GPU-writeback u32 with nothing but
& ptr_mask (cik_ih.c:205). The consumer walk (amdgpu_ih_process,
amdgpu_ih.c:154-157) terminates on rptr == wptr while rptr advances
strictly in 16-byte steps (+= 16 at cik_ih.c:255 prescreen-drop and
cik_ih.c:287 decode). Two consequences for a wptr that is not a multiple
of 16 (hostile/faulting firmware DMA, corrupted writeback after a PCIe
fault):
- Permanent interrupt-thread spin (the DF-4843 core). With the
overflow bit clear,
rptr(phase 0) can never equal a phase-p wptr: the ithread laps the 64 KiB ring forever β 4096 IVs re-dispatched per lap β withih->lockheld (amdgpu_ih.c:144) andrestart_ihre-reading the same bad wptr (amdgpu_ih.c:163-165). One CPU pegged in interrupt context, all GPU interrupt delivery dead. - OOB read aggravator (the DF-4854 shape, 12-byte variant). With the
overflow bit SET and wptr' (overflow-cleared) in
[0xFFE4..0xFFEF](sub-16 phase 4..15 near ring end), the recoveryrptr = (wptr' + 16) & 0xFFFFlands in[0xFFF4..0xFFFF]β within 12 bytes of the ring end. The firstprescreen_iv/decode_ivthen readsring[ring_index .. ring_index + 3]withring_index = rptr>>2up to0x3FFF(cik_ih.c:243-246, 274-277) β dwords0x4000..0x4002= up to 12 bytes past the 64 KiB ring buffer. The ring here is aamdgpu_bo_create_kernelGTT BO of exactlyring_size(amdgpu_ih.c:80-88) β unlike vega10'sring_size+8dma_alloc_coherent, there is zero slack: the read touches the next mapped page (adjacent kernel memory interpreted as an IV and handed toamdgpu_amdkfd_interruptat amdgpu_irq.c:170) or faults outright. The garbage IV is then decoded and dispatched to IP-block handlers (dispatch indices themselves are bounds-checked in amdgpu_irq.c, so the blast radius is the 12-byte OOB read + bogus dispatch).
Prescreen/PASID (cik_ih.c:238-257): same fault-credit logic as tonga β
amdgpu_vm_pasid_fault_credit (amdgpu_vm.c:3370-3397) is idr_find under
pasid_lock; the drop path advances rptr by 16 consistently with the
walk. No cik-unique defect there.
Threat model / severity
Hostile or faulting GPU device (the wptr source is device DMA into host writeback memory). No unprivileged-software trigger; hardware-gated DoS + bounded OOB read. Same Low grading as DF-4843/DF-4854.
Reproduce
Not runnable on the audit guest: guest PCI tree has no AMD GPU (env.txt), and the trigger requires device-side DMA of a misaligned wptr. Code-certain by inspection (arithmetic above). See trigger_sketch.c for the hostile write pattern that would be needed.
Fix
In cik_ih_get_wptr, after clearing the overflow bit, normalize a
misaligned wptr down to the 16-byte entry boundary (wptr &= ~0xF; after
masking, with a dev_warn), and compute the overflow-recovery rptr from
the aligned value β plus a per-invocation entry cap in
amdgpu_ih_process so a storm or bad wptr can never hard-spin the ithread.
DF-4887 VERDICT
Status: untested (device-gated; the audit guest has no AMD GPU β
pciconf -l shows no AMD/ATI device, env.txt). Identical treatment to the
just-filed twins DF-4843 (tonga_ih.c) and DF-4854 (vega10_ih.c): the
defect's data source is device DMA into host memory, which no guest
without the hardware can produce.
Certain by inspection
- wptr source is the writeback buffer, same as tonga β this is the
third instance of the family, not a different shape.
cik_ih_get_wptr(cik_ih.c:190) readsadev->wb.wb[adev->irq.ih.wptr_offs];use_bus_addris false for CIK (cik_ih.c:321), so only the wb path exists. Returned with nothing butwptr & adev->irq.ih.ptr_mask(cik_ih.c:205) β no 16-byte alignment validation. - The consumer walk steps rptr strictly 16 bytes.
amdgpu_ih_process(amdgpu_ih.c:154-157) loopswhile (ih->rptr != wptr); cik advances rptr by exactly 16 in bothprescreen_iv(cik_ih.c:255) anddecode_iv(cik_ih.c:287). A wptr with phase != 0 (mod 16) and no overflow bit can never equal rptr β permanent ithread spin: 4096 IVs re-dispatched per lap of the 64 KiB ring,ih->lockheld (amdgpu_ih.c:144),restart_ihre-reading the same wptr (amdgpu_ih.c:163-165). One CPU pegged, all GPU interrupt delivery dead. (Note: if the overflow bit IS set, the recovery transfers the wptr's phase into rptr, and after at most one full lap rptr does reach wptr β the unbounded spin specifically needs the no-overflow misaligned case, same as DF-4843.) - OOB aggravator (DF-4854 shape, 12-byte variant, zero-slack BO).
Overflow recovery (cik_ih.c:192-204) computes
rptr = (wptr + 16) & 0xFFFF(cik_ih.c:200) from the unaligned wptr. With wptr' in [0xFFE4..0xFFEF] (phase 4..15, overflow set), rptr lands in [0xFFF4..0xFFFF];prescreen_iv/decode_ivthen readring[ring_index .. ring_index+3],ring_index = rptr>>2up to 0x3FFF (cik_ih.c:243-246, 274-277) β dwords 0x4000..0x4002 = up to 12 bytes past the 64 KiB ring. The CIK ring is aamdgpu_bo_create_kernelGTT BO of exactlyring_size(amdgpu_ih.c:80-88) β no+8slack (that slack exists only in theuse_bus_addrcoherent path, amdgpu_ih.c:61-68). The 12 OOB bytes are decoded as an IV, forwarded toamdgpu_amdkfd_interrupt(amdgpu_irq.c:170) and dispatched; dispatch indices are separately bounds-checked in amdgpu_irq.c, so the blast radius is the OOB read + a bogus dispatch (worst case: adjacent mapped page data influencing which handler runs, or a fault on unmapped vmalloc space β panic).
Batch questions answered
- wb vs direct reg: wb (cik_ih.c:190) β same as tonga; cik_ih never
reads
mmIH_RB_WPTRfor polling. - same RB_OVERFLOW path: yes β cik_ih.c:192-204,
+16recovery like tonga (+32on vega10's 32-byte grid). - doorbell: none for CIK IH β funcs table (cik_ih.c:462-467) has no
doorbell member of interest;
set_rptrusesmmIH_RB_RPTR(cik_ih.c:297-300). - prescreen/pasid/idr (cik_ih.c:238-257): same
amdgpu_vm_pasid_fault_creditcall as tonga (amdgpu_vm.c:3370-3397, idr_find under pasid_lock); the credit-drop path advances rptr by 16 β consistent with the walk; no cik-unique defect.!pasidβ process (kernel-context fault), matching tonga/upstream. - process-loop count cap: none in the shared walk (amdgpu_ih.c:154) β inherited by all ASICs; already covered by DF-4843's cap recommendation; fix.diff here keeps to this file's get_wptr plus the shared-loop cap.
Why Low / not uid0
The corrupted wptr must come from device DMA (hostile or faulting GPU / PCIe fault). No unprivileged local software path writes the wb slot. The OOB read is β€ 12 bytes, read-only, and its content only steers existing IRQ handlers whose own indices are validated. Impact ceiling: permanent GPU-interrupt-thread spin (DoS) + tiny OOB read β Low, consistent with DF-4843/DF-4854.
Fix
fix.diff: align-normalize the wptr after overflow-bit clear
(wptr &= ~0xF with a dev_warn on mismatch, and derive the recovery rptr
from the aligned value), plus a per-invocation entry cap in the shared
walk so no bad wptr can hard-spin the ithread.
Fix validation
Not possible on this guest (no CIK hardware) β fix_status: not_testable;
the diff is authored against the read-only sys/ tree and never applied
there.
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 possible: finding is device-gated (no CIK GPU on the audit guest); fix mirrors the DF-4843/DF-4854 recommendation (align + walk cap), static-only
fix.diff (git-apply-able vs read-only sys/ tree; never applied); VERDICT.md 'Fix' section
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/cik_ih.c:190
- sys/dev/drm/amd/amdgpu/cik_ih.c:192
- sys/dev/drm/amd/amdgpu/cik_ih.c:200
- sys/dev/drm/amd/amdgpu/cik_ih.c:205
- sys/dev/drm/amd/amdgpu/cik_ih.c:240
- sys/dev/drm/amd/amdgpu/cik_ih.c:246
- sys/dev/drm/amd/amdgpu/cik_ih.c:255
- sys/dev/drm/amd/amdgpu/cik_ih.c:274
- sys/dev/drm/amd/amdgpu/cik_ih.c:287
- sys/dev/drm/amd/amdgpu/cik_ih.c:321
- sys/dev/drm/amd/amdgpu/amdgpu_ih.c:80
- sys/dev/drm/amd/amdgpu/amdgpu_ih.c:144
- sys/dev/drm/amd/amdgpu/amdgpu_ih.c:154
- sys/dev/drm/amd/amdgpu/amdgpu_ih.c:163
- sys/dev/drm/amd/amdgpu/amdgpu_irq.c:170
- sys/dev/drm/amd/amdgpu/amdgpu_vm.c:3370
Detail
Evidence (decisive lines)
README.md (batch questions answered + exact arithmetic with path:line); VERDICT.md (proof + fix rationale); trigger_sketch.c (hostile-device write pattern for both defects); fix.diff (align-normalize + shared-walk cap)
PoC changes
sketch only; no runnable PoC without CIK hardware (device-DMA wptr source)
Verified recommended fix
in cik_ih_get_wptr, align-normalize the wptr to the 16-byte IV boundary after clearing RB_OVERFLOW (warn on mismatch) and derive the overflow-recovery rptr from the aligned value; cap per-invocation entries in amdgpu_ih_process so a bad wptr or storm cannot hard-spin the interrupt thread
Verdict
Not runnable on the audit guest (no AMD GPU; the wptr source is device DMA into the host writeback slot, which software on the guest cannot produce). Code-certain by inspection - third instance of the just-filed DF-4843/DF-4854 family, confirmed SAME shape, not different: cik_ih_get_wptr reads the wb slot (cik_ih.c:190; use_bus_addr=false from cik_ih.c:321, so wb is the only path - CIK has NO IH doorbell, set_rptr uses mmIH_RB_RPTR at cik_ih.c:297-300) and returns it with nothing but &ptr_mask (cik_ih.c:205) - 16-byte alignment never validated. Core defect: amdgpu_ih_process (amdgpu_ih.c:154-157) walks with rptr stepping strictly 16 bytes (cik_ih.c:255 prescreen-drop, cik_ih.c:287 decode); a misaligned wptr without the overflow bit can never equal rptr - the amdgpu interrupt thread laps the 64KiB ring forever (4096 IVs re-dispatched/lap) with ih->lock held (amdgpu_ih.c:144) and restart_ih re-reading the same wptr (amdgpu_ih.c:163-165): one CPU pegged, all GPU IRQ delivery dead. Aggravator (DF-4854 shape, 12-byte variant): with RB_OVERFLOW set and overflow-cleared wptr in [0xFFE4..0xFFEF] (sub-16 phase 4..15 near ring end), the recovery rptr=(wptr+16)&0xFFFF (cik_ih.c:200) lands in [0xFFF4..0xFFFF], so the first prescreen/decode (cik_ih.c:243-246, 274-277) reads ring[ring_index..ring_index+3] with ring_index up to 0x3FFF -> dwords 0x4000..0x4002 = up to 12 bytes PAST the ring - and the CIK ring is an amdgpu_bo_create_kernel GTT BO of exactly ring_size (amdgpu_ih.c:80-88) with ZERO slack (the +8 slack only exists in the use_bus_addr path, amdgpu_ih.c:61-68), unlike vega10's 20B-past-a-slacked-allocation. OOB bytes are decoded as an IV, forwarded to amdgpu_amdkfd_interrupt (amdgpu_irq.c:170) and dispatched (dispatch indices separately bounds-checked). Prescreen/PASID (cik_ih.c:238-257) matches tonga: amdgpu_vm_pasid_fault_credit (amdgpu_vm.c:3370-3397) idr_find under pasid_lock, drop path advances rptr by 16 consistently - no cik-unique defect. Hostile/faulting-device model: DoS + <=12-byte read-only OOB read; no uid0 route; Low, consistent with the twins.
No comments yet.