iwm_handle_rxb: PAGE_SIZE sc_cmd_resp copy from possibly-offset response packet reads up to ~4KB past the 4096-byte RX cluster; iwm_alive_fn v3 fallback reads fixed struct without payload-length check
Summary
On NVM_ACCESS/MCC_UPDATE command responses, iwm_handle_rxb copies sizeof(sc->cmd_resp)==PAGE_SIZE==4096 bytes from pkt regardless of packet length or offset; HAVEROOM() permits packet offsets up to 4087 inside the 4096-byte RX cluster, and multi-notification buffers are normal firmware behavior, so the copy spans up to offset bytes past the mbuf cluster end. This buffer becomes resp_pkt for every synchronous command and is parsed by NVM/MCC consumers. Sibling sink: iwm_alive_fn's v3 fallback reads fixed struct offsets from the raw notification packet with no payload-length check. Distinct from DF-1145 (MPDU lengths/phy/stats sinks). Firmware-controlled RX buffer layout: heap OOB read of up to ~4KB past a 4KB cluster into driver state consumed for NVM/regulatory parsing and a console-printed MAC. Fix: clamp copy to actual packet length + validate ALIVE payload.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3630 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| env.txt | β | 740 B | view raw | |
| fix.diff | β | 905 B | view raw | |
| manifest.json | β | 966 B | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-3630 β iwm_handle_rxb: unconditional PAGE_SIZE copy of command responses reads past the 4KB RX cluster
Where
sys/dev/netif/iwm/if_iwm.c:5509-5515 (NVM_ACCESS/MCC_UPDATE response path)
and :5572-5577 (long-command response path) inside iwm_handle_rxb();
consumer iwm_send_cmd()/iwm_free_resp() at
sys/dev/netif/iwm/if_iwm_util.c:327,369.
What
uint32_t maxoff = IWM_RBUF_SIZE; /* 4096 (if_iwmvar.h:290) */
#define HAVEROOM(a) ((a) + sizeof(uint32_t) + sizeof(struct iwm_cmd_header) < maxoff)
while (HAVEROOM(offset)) { /* offset can be up to 4087 */
struct iwm_rx_packet *pkt = mtodoff(m, ..., offset);
...
case IWM_NVM_ACCESS_CMD:
case IWM_MCC_UPDATE_CMD:
if (sc->sc_wantresp == (((qid & ~0x80) << 16) | idx)) {
memcpy(sc->sc_cmd_resp,
pkt, sizeof(sc->sc_cmd_resp)); /* PAGE_SIZE == 4096 ! */
}
sc_cmd_resp is uint8_t sc_cmd_resp[IWM_CMD_RESP_MAX] with
IWM_CMD_RESP_MAX = PAGE_SIZE (if_iwmvar.h:312,500). The copy length is
therefore 4096 bytes regardless of the actual firmware packet length or its
offset inside the RX mbuf. The mbuf is a single IWM_RBUF_SIZE (4096-byte)
cluster (iwm_rx_addbuf, if_iwm.c:3044-3055). A response packet that does
not start at offset 0 β entirely normal, since the firmware may prepend other
notifications to the same receive buffer, and iwm_handle_rxb explicitly
walks multiple packets per buffer β makes the copy run up to offset bytes
past the end of the cluster (offset max 4087 β up to ~4KB heap OOB read).
The copied bytes are then served to synchronous command consumers as
cmd->resp_pkt (if_iwm_util.c:327) and parsed, e.g. NVM chunk parsing
reads nvm_resp->length/data from it. Data does not go directly to
userspace, but the OOB heap read is real kernel-memory disclosure into
driver state (and crash potential when the cluster ends at an unmapped
boundary).
Same class, same fix pattern: iwm_alive_fn() (if_iwm.c:2768-2789) β
the notification-wait callback receives the raw packet
(iwm_notification_wait_notify(...) at :5407) and its legacy-v3 fallback
branch (:2784-2789) reads palive3 fields with no
iwm_rx_packet_payload_len() minimum check (the v4 branch only checks
== sizeof(*palive)), an OOB read when the firmware emits a truncated ALIVE.
Not covered by DF-1145: that finding covers MPDU byte_count/mpdu_len,
iwm_rx_rx_phy_cmd and iwm_handle_rx_statistics β not the sc_cmd_resp
copies nor iwm_alive_fn.
Threat model
Malicious/errant firmware DMA content (same trust boundary as DF-1145, which was rated High). Multi-notification RX buffers are a normal firmware behavior, so even non-malicious short responses placed after another notification trigger the overread.
Reproduce
Kernel path needs iwm hardware (absent on guest β see env.txt). Arithmetic:
copy spans [m_data+offset, m_data+offset+4096) while the cluster is
[m_data, m_data+4096); overrun == offset bytes.
Fix
fix.diff β clamp the copy to the actual packet length (len, already
computed at :5403-5404) capped by sizeof(sc->sc_cmd_resp), and require a
minimum payload length in the iwm_alive_fn v3 fallback.
DF-3630 VERDICT
Status: inconclusive (kernel path not exercisable β no iwm hardware on guest). Confidence in the defect: certain (source-level arithmetic).
Verified in source
sc_cmd_respisuint8_t[IWM_CMD_RESP_MAX],IWM_CMD_RESP_MAX == PAGE_SIZE= 4096 (sys/dev/netif/iwm/if_iwmvar.h:312,500).- RX buffers are 4096-byte clusters:
iwm_rx_addbuf()allocatesm_getjcl(M_WAITOK, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE)(sys/dev/netif/iwm/if_iwm.c:3053),IWM_RBUF_SIZE == 4096(if_iwmvar.h:290). iwm_handle_rxb()walks multiple firmware packets per buffer starting at arbitraryoffsetbounded only byHAVEROOM(offset)= offset+8 < 4096 (if_iwm.c:5372-5405), i.e. offset β€ 4087.- On
IWM_NVM_ACCESS_CMD/IWM_MCC_UPDATE_CMDresponse match (if_iwm.c:5509-5514) it copiessizeof(sc->sc_cmd_resp)= 4096 bytes frompktβ a copy span of[offset, offset+4096)in a 4096-byte cluster β up to 4087 bytes past the end of the mbuf cluster whenever the response packet does not sit at offset 0. The packet's own length (len = iwm_rx_packet_len(pkt) + 4,:5403-5404) is computed but never used to bound this copy. - The copied buffer becomes
hcmd->resp_pkt(if_iwm_util.c:327) and is parsed by consumers (e.g.iwm_nvm_read_chunkif_iwm.c:1856-1903). - Sibling:
iwm_alive_fn()v3 fallback (if_iwm.c:2784-2789) reads a fixed-size struct from the raw notification packet without any payload length check (contrast the correct pattern at:5535for DTS_MEASUREMENT and:2778for the v4 size match).
Why not reproduced on the guest
No Intel Wireless PCI device exists (pciconf empty; kldload if_iwm
attaches nothing β env.txt). The RX path cannot be driven without the
device. The overread is pure arithmetic: overrun bytes == packet offset
within the cluster.
Impact ceiling
Kernel heap OOB read (up to ~4KB past a 4KB mbuf cluster) into driver state
consumed for NVM/regulatory parsing; contents can reach logs
(ether_sprintf(sc->nvm_data->hw_addr) at if_iwm.c:6389 prints a MAC
derived from parsed data). Potential crash if the cluster ends at an
unmapped boundary. Requires firmware-controlled RX placement β same trust
boundary as DF-1145 (High).
Fix validation
fix.diff authored; in-kernel validation impossible (no device) β 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 clamps the copy to the computed packet length (len, if_iwm.c:5403-5404) and adds an alive_fn payload check; applies cleanly; no device to validate in-kernel
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; README.md (copy-span arithmetic); env.txt; fix.diff (length-clamped copy + alive_fn minimum-length check, applies clean)
PoC changes
n/a - source-level verification
Verified recommended fix
fix.diff clamps the copy to the computed packet length (len, if_iwm.c:5403-5404) and adds an alive_fn payload check
Verdict
iwm_handle_rxb() copies sizeof(sc->sc_cmd_resp)==PAGE_SIZE (4096) bytes from a command response packet (if_iwm.c:5511-5514) regardless of the packet's actual length or its offset in the 4096-byte RX cluster; HAVEROOM() allows offset up to 4087 (:5377-5380,5405), so a response preceded by any other notification overreads up to ~4087 bytes past the mbuf cluster. The copy is the source of every synchronous command response (hcmd->resp_pkt, if_iwm_util.c:327). Sibling: iwm_alive_fn() v3 fallback (:2784-2789) reads a fixed struct from the raw packet with no payload-length check. Distinct from DF-1145 (MPDU byte_count/mpdu_len, phy-cmd, statistics sinks). Not exercisable on the guest (no iwm device; env.txt).
No comments yet.