Firmware ring indices used unmasked: iwi_tx_intr/iwi_rx_intr termination depends on a raw CSR-read index; an out-of-range value β non-terminating interrupt loop holding IWI_LOCK + wlan serializer (hard hang) and txq->queued int underflow
Summary
hw = CSR_READ_4(sc, txq->csr_ridx) and CSR_READ_4(IWI_CSR_RX_RIDX) are never masked into the ring domain, while the counters advance modulo IWI_TX/RX_RING_COUNT. If the firmware (or a wedged card - the driver itself expects fw desyncs per the watchdog) publishes hw outside [0,COUNT), the loops never terminate inside the interrupt handler with the lock held -> hard system hang; the tx loop also re-completes drained slots and underflows txq->queued. Hostile/buggy firmware or bus-error wedge on any iwi machine -> local hard hang. iwn(4) masks these; iwi does not. Mask-at-read fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3842 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 1.2 KB | β raw | |
| VERDICT.md | file | 2.3 KB | β raw | |
| build.log | file | 85 B | view raw | |
| build.sh | file | 82 B | view raw | |
| env.txt | file | 286 B | view raw | |
| fix.diff | file | 525 B | view raw | |
| manifest.json | file | 562 B | view raw | |
| run.log | file | 959 B | view raw | |
| run.sh | file | 90 B | view raw | |
| trigger.c | file | 1.6 KB | view raw | |
| verdict.json | file | 2.8 KB | view raw |
DF-3842 β iwi(4) trusts firmware ring indices without range validation: iwi_tx_intr() (if_iwi.c:1681-1694) and iwi_rx_intr() (if_iwi.c:1639-1668) compare a raw CSR-read index against a ring counter that is always taken modulo the ring size. An out-of-range index (hostile/buggy firmware or a wedged card) makes the loops non-terminating while IWI_LOCK and the wlan serializer are held -> hard system hang; the tx loop additionally re-completes already-completed slots (ni/m NULL, txq->queued int underflow).
WHAT TO LOOK AT trigger.c documentation harness with both loops quoted. fix.diff mask hw with % IWI_{TX,RX}_RING_COUNT at read time.
BUILD cc -O2 -o trigger trigger.c (compiles clean on the guest) RUN ./trigger (stub: hardware-gated path) EXPECT On hardware: wedge the card (e.g. rfkill toggle storm until a command timeout desynchronises the fw ring) or run hostile fw: the interrupt handler never returns; console dead; ping dies.
CALIBRATION Low / likely β needs fw/bus misbehaviour as the trigger, but the missing mask is unambiguous in source and every other driver in the tree masks or bounds-checks these reads.
DF-3842 VERDICT β status: inconclusive (hardware-gated), source-level: LIKELY
Source trace (if_iwi.c):
iwi_tx_intr :1681 hw = CSR_READ_4(sc, txq->csr_ridx); / fw value /
:1683 while (txq->next != hw) {
:1684 data = &txq->data[txq->next]; / in-bounds /
:1689 ieee80211_tx_complete(data->ni, data->m, 0);
ieee80211_tx_complete() tolerates ni == NULL
(sys/netproto/802_11/wlan/ieee80211_output.c:3680)
and m_freem(NULL) is a no-op, so the second lap over
the ring is "safe" per iteration β
:1692 txq->queued--; / int underflow /
:1693 txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; (64)
If hw >= 64 (or any value not in [0,64)), next cycles 0..63 forever and
never equals hw: the interrupt handler spins holding IWI_LOCK and the
wlan serializer -> hard hang (no timer, no second intr can make progress).
iwi_rx_intr :1639-1667 has the identical pattern with IWI_RX_RING_COUNT
(32) and additionally re-runs iwi_frame_intr over swapped mbufs each lap.
Preconditions: the firmware publishes an out-of-range read index β hostile fw, or a desynchronised/wedged card (the driver itself acknowledges fw desyncs: iwi_watchdog :2093 "device timeout" -> restart task). Reads of 0xffffffff from a vanished device are the classic source; note iwi_intr() guards only the INTR register against 0xffffffff (if_iwi.c:1733), not the ring index registers.
Why not reproduced: no iwi NIC on the audit guest (run.log). The hang is conditional on device misbehaviour, hence confidence "likely" rather than "certain" β the missing mask itself is unambiguous.
Severity: Low (DoS of the local system, preconditioned on fw/bus fault).
Fix: fix.diff β mask both index reads into the ring domain at read time. Fix validation: not possible on this guest β fix_status not_applicable.
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 impossible on this guest: the vulnerable device (AMD radeon CS / Intel 2200BG) does not exist, so neither baseline nor patched kernel can exercise the path. fix.diff authored against the read-only sys/ tree, never applied.
fix.diff in this pack; structurally mirrors the bounds checks the same driver family already enforces elsewhere (radeon_cs_packet_parse fit check; iwn ring-index masking).
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md (what to look at), VERDICT.md (line-by-line trace), trigger.c/mkbadfw.c, build.log (clean guest compile, gcc 8.3), run.log (honest ENOENT / generator RUN_OK), env.txt (guest has no DRI, no iwi NIC), fix.diff
PoC changes
PoC authored fresh for this finding (cross-file family seed): raw DRM ioctl ABI (cmd low byte 0x66 dispatches RADEON_CS on DF drm_ioctl.c:809), chunk structs and packet encodings verified against sys/dev/drm/include/uapi/drm/radeon_drm.h; iwi mkbadfw.c emits the v3 combined-image header per if_iwireg.h:161-166.
Verified recommended fix
Mask both ring index reads with % IWI_TX_RING_COUNT / % IWI_RX_RING_COUNT at read time.
Verdict
Likely (preconditioned on firmware/bus misbehaviour), runtime inconclusive (no iwi NIC on guest). hw = CSR_READ_4(csr_ridx) is never masked into [0,IWI_TX_RING_COUNT); the mod-64 counter can never equal an out-of-range hw, so the while-loop spins forever inside the interrupt handler with the lock held. ieee80211_tx_complete tolerates the NULL ni/m of repeat laps (ieee80211_output.c:3680) but txq->queued underflows and the system hangs. iwi_intr only guards the INTR register against 0xffffffff (:1733), not the ring registers. iwn(4) masks these reads; iwi does not.
No comments yet.