iwm_rx_tx_cmd: firmware-supplied qid indexes sc->txq[IWM_MAX_QUEUES=31] with no bounds check - OOB ring access, NULL writes at OOB offsets, wild node deref (iwm twin of DF-1122)
Summary
Every IWM_TX_CMD completion from the firmware is dispatched to iwm_rx_tx_cmd(), which copies qid/idx straight from the device-DMA'd command header and immediately does ring=&sc->txq[qid]; txd=&ring->data[idx]. sc->txq[] has IWM_MAX_QUEUES==31 entries while qid is a uint8_t, so qid in 31..255 accesses a struct iwm_tx_ring far past the array inside struct iwm_softc (~0x4000 bytes each - qid=255 lands ~1.4MB past); the function then writes txd->done=1, txd->m=NULL, txd->in=NULL, decrements the OOB ring->queued, computes 1<<qid with qid>=32 (UB), and dereferences the wild in pointer in iwm_rx_tx_cmd_single and ieee80211_tx_complete. Malicious/buggy Intel ucode (or a PCIe device spoofing an iwm PCI ID) DMA'ing a TX completion with qid 31..255: write-capable kernel memory corruption plus wild-pointer derefs. Hardware-gated on this guest (kldload if_iwm attaches nothing); source-certain. Fix: qid/idx bounds check + drop.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3629 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.7 KB | β raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| env.txt | β | 740 B | view raw | |
| fix.diff | β | 472 B | view raw | |
| manifest.json | β | 985 B | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-3629 β iwm_rx_tx_cmd: firmware-supplied qid/idx index sc->txq[]/ring->data[] without bounds checks
Where
sys/dev/netif/iwm/if_iwm.c:3588-3632 (iwm_rx_tx_cmd), reached from
iwm_handle_rxb() at sys/dev/netif/iwm/if_iwm.c:5453-5455 for every
IWM_TX_CMD (0x1c) notification delivered by the firmware.
What
idx = cmd_hdr->idx; /* uint8_t: 0..255 */
qid = cmd_hdr->qid; /* uint8_t: 0..255 */
ring = &sc->txq[qid]; /* sc->txq[IWM_MAX_QUEUES == 31] (if_iwmvar.h:447, if_iwmreg.h:1923) */
txd = &ring->data[idx]; /* ring->data[IWM_TX_RING_COUNT == 256] */
in = txd->in; /* wild pointer for qid >= 31 */
m = txd->m;
cmd_hdr is the iwm_cmd_header of an RX packet that the device DMA'd into
host memory β a fully firmware-controlled byte pattern. There is no
validation of qid against nitems(sc->txq) (31) nor idx against
IWM_TX_RING_COUNT (256). idx happens to be safe (uint8_t < 256), but any
qid in 31..255 indexes past the end of sc->txq[] inside struct
iwm_softc. Consequences on the OOB ring:
- iwm_rx_tx_cmd_single(sc, pkt, in) dereferences in->in_ni.ni_vap,
vap->iv_bss->ni_txrate β wild-pointer reads;
- bus_dmamap_sync/unload(ring->data_dmat, txd->map, ...) with garbage
tag/map values;
- writes: txd->done = 1; txd->m = NULL; txd->in = NULL;
(if_iwm.c:3620-3622) at attacker(chosen-queue)-offset memory, plus
sc->qfullmsk &= ~(1 << qid) (undefined shift for qid β₯ 32) and
--ring->queued on OOB memory;
- ieee80211_tx_complete(&in->in_ni, m, status) on a wild node pointer β
UAF-style secondary corruption.
This is the iwm twin of the iwn finding DF-1122
(iwn_rx_compressed_ba unbounded qid), same CWE-129/CWE-787 class.
Threat model
Requires malicious, buggy, or compromised firmware (or a Thunderbolt/PCIe device spoofing the iwm PCI ID). Intel ucode has shipped length/index inconsistencies before (the DF-1145 class). The device DMA content is untrusted from the driver's perspective; the driver must validate indices before array use. This bug is write-capable, unlike the DF-1145 read.
Reproduce (kernel path)
Not possible on the audit guest: no Intel Wireless PCI device is present
(pciconf -l shows none; kldload if_iwm attaches nothing). Source-level
certainty: see VERDICT.md. The index arithmetic is:
qid = 255 -> &sc->txq[255] = sc + offsetof(iwm_softc, txq)
+ 255 * sizeof(struct iwm_tx_ring)
(224 * sizeof(iwm_tx_ring) bytes past the array end;
sizeof(iwm_tx_ring) β 0x4000+ incl. 256 iwm_tx_data)
Fix
fix.diff in this directory (bounds-check qid/idx, drop the completion).
DF-3629 VERDICT
Status: inconclusive (kernel path not exercisable β no iwm hardware on guest). Confidence in the defect: certain (source-level).
What was verified
- Source trace (certain).
iwm_handle_rxb()dispatches firmware notifications onpkt->hdr.codewith no validation ofpkt->hdr.qid/pkt->hdr.idx(sys/dev/netif/iwm/if_iwm.c:5385-5455). ForIWM_TX_CMDit callsiwm_rx_tx_cmd(sc, pkt)(:5453-5455), which immediately doesring = &sc->txq[qid]; txd = &ring->data[idx];(:3598-3604).sc->txq[]hasIWM_MAX_QUEUES == 31elements (sys/dev/netif/iwm/if_iwmreg.h:1923,sys/dev/netif/iwm/if_iwmvar.h:447);qidis a rawuint8_tcopied from device-DMA'd memory. Values 31..255 are out of bounds. The function then writes through the OOBtxd(txd->done = 1; txd->m = NULL; txd->in = NULL;at:3620-3622), decrements OOBring->queued(:3627), shifts1 << qidwith qid β₯ 32 (undefined behavior,:3626-3628), and passes the wildinpointer intoiwm_rx_tx_cmd_single()(:3612, derefs:3480-3483) andieee80211_tx_complete()(:3624).idxitself cannot overflow (uint8_t < IWM_TX_RING_COUNT == 256), the bug is the missingqidbound. - Guest reality (why not reproduced). DragonFly 6.5-DEVELOPMENT guest:
if_iwm.koexists andkldload if_iwmsucceeds, but there is no Intel Wireless PCI device β the driver attaches to nothing, no wlan interface exists, and no RX/TX completion path can ever run (env.txt). Triggering requires either an iwm NIC whose firmware emits a TX completion with a bogus qid, or an attacker-controlled PCIe device presenting an iwm PCI ID.
Exploit chain (for the class)
malicious/errant firmware (or hostile PCIe device) -> DMA an RX buffer whose
packet header has code=IWM_TX_CMD, qid in 31..255 -> OOB txd at a
predictable offset (255-31)*sizeof(iwm_tx_ring) past sc->txq -> NULL/1
writes at chosen ring granularity + wild node pointer deref -> kernel memory
corruption/panic; with heap grooming of the softc-adjacent allocation this
is a write primitive. Not demonstrable further without the device.
Precedent
DF-1122 (iwn iwn_rx_compressed_ba, identical missing qid bound, severity
High). FreeBSD's later if_iwm grew the same class of checks.
Fix validation
fix.diff authored (bounds check + drop). In-kernel validation impossible on this guest (no device to generate TX completions) β 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 (qid/idx bounds check + drop) applies cleanly to sys/dev/netif/iwm/if_iwm.c; in-kernel validation impossible - no iwm device on guest to generate TX completions
fix.diff verified with patch --dry-run against sys/dev/netif/iwm/if_iwm.c; no kernel-level validation possible (no iwm device)
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (source trace + guest reality); README.md (arithmetic); env.txt (no-device proof); fix.diff (applies clean)
PoC changes
n/a - source-level verification; no runnable kernel PoC possible without iwm hardware
Verified recommended fix
fix.diff (qid/idx bounds check + drop) applies cleanly to sys/dev/netif/iwm/if_iwm.c
Verdict
iwm_rx_tx_cmd() indexes sc->txq[qid]/ring->data[idx] with qid/idx taken straight from the firmware-DMA'd command header (if_iwm.c:3597-3604) with no bounds check; sc->txq[] has IWM_MAX_QUEUES==31 entries (if_iwmvar.h:447, if_iwmreg.h:1923) while qid is a uint8_t, so qid in 31..255 is an OOB ring access that is then WRITTEN (txd->done=1, txd->m=NULL, txd->in=NULL at :3620-3622; --ring->queued at :3627) and dereferenced (wild in->in_ni at :3480-3483,:3624). iwn twin of DF-1122 (High). Not exercisable on the audit guest: if_iwm.ko loads but no Intel Wireless PCI device exists, so the TX-completion path never runs (env.txt). Source-level certainty: the arithmetic and the write sites are unconditional on that path.
No comments yet.