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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3629 Β· 6 files
FileTypeDescriptionSize
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).

VERDICT.md
↓ download raw

DF-3629 VERDICT

Status: inconclusive (kernel path not exercisable β€” no iwm hardware on guest). Confidence in the defect: certain (source-level).

What was verified

  1. Source trace (certain). iwm_handle_rxb() dispatches firmware notifications on pkt->hdr.code with no validation of pkt->hdr.qid/pkt->hdr.idx (sys/dev/netif/iwm/if_iwm.c:5385-5455). For IWM_TX_CMD it calls iwm_rx_tx_cmd(sc, pkt) (:5453-5455), which immediately does ring = &sc->txq[qid]; txd = &ring->data[idx]; (:3598-3604). sc->txq[] has IWM_MAX_QUEUES == 31 elements (sys/dev/netif/iwm/if_iwmreg.h:1923, sys/dev/netif/iwm/if_iwmvar.h:447); qid is a raw uint8_t copied from device-DMA'd memory. Values 31..255 are out of bounds. The function then writes through the OOB txd (txd->done = 1; txd->m = NULL; txd->in = NULL; at :3620-3622), decrements OOB ring->queued (:3627), shifts 1 << qid with qid β‰₯ 32 (undefined behavior, :3626-3628), and passes the wild in pointer into iwm_rx_tx_cmd_single() (:3612, derefs :3480-3483) and ieee80211_tx_complete() (:3624). idx itself cannot overflow (uint8_t < IWM_TX_RING_COUNT == 256), the bug is the missing qid bound.
  2. Guest reality (why not reproduced). DragonFly 6.5-DEVELOPMENT guest: if_iwm.ko exists and kldload if_iwm succeeds, 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_testable
baseline no→ patch + rebuild →patched clean

fix.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)
↓ fix.diffper-fix-DF-3629

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.