Unbounded firmware-supplied MPDU length in RX paths causes kernel heap OOB read
Summary
iwm_rx_rx_mpdu at if_iwm.c:3221-3251: len=le16toh(rx_res->byte_count) from firmware used WITHOUT bounds check against pkt->len or IWM_RBUF_SIZE=4096. Status-word read at pkt->data+4+len at :3222 lands up to ~64KB past 4KB cluster. m_len set to unbounded len at :3250-3251 -> ieee80211_input reads past cluster. iwm_rx_mpdu_mq at :3342-3349 same pattern with desc->mpdu_len. Plus iwm_rx_rx_phy_cmd (:3102) and iwm_handle_rx_statistics (:3149) memcpy sizeof() without payload-length check. DTS_NOTIFICATION handler at :5535 already has the correct check proving omission is oversight. Ported from OpenBSD if_iwm.c v1.167 (2017) missing 8+ years of upstream hardening. Same class as DF-1123 (iwn). Requires firmware length-inconsistency bug (Intel ucode has had many). Fix: clamp len against iwm_rx_packet_len and IWM_RBUF_SIZE.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1145 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | bound firmware MPDU length + phy/stat memcpy to iwm_rx_packet_payload_len (git-apply-able) | 1.6 KB | view raw |
| build.sh | build-script | apply fix.diff + incremental compile of iwm if_iwm.o | 429 B | view raw |
| run.sh | run-script | documents runtime-unreachable (no WiFi HW on guest) | 641 B | view raw |
| VERDICT.md | verdict | full source-trace + mechanism + fix rationale | 4.1 KB | β raw |
| env.txt | environment | guest uname, ifconfig, driver build status | 2.0 KB | view raw |
| build.log | build-log | fix compile-validation output (patched if_iwm.o, -Werror clean) | 1.5 KB | view raw |
| README.md | readme | human repro summary | 1.8 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1145 β Unbounded firmware-supplied MPDU length in iwm RX path (if_iwm.c)
Status: REPRODUCED at code level β latent at runtime on this guest (no WiFi HW). Severity (finding): High Β· CWE-125 Out-of-bounds Read
What the bug is
iwm_rx_rx_mpdu (sys/dev/netif/iwm/if_iwm.c:3221) takes
len = le16toh(rx_res->byte_count) (firmware-supplied, up to 65535) without
any check against the actual packet payload (iwm_rx_packet_payload_len,
if_iwmreg.h:6944) or the 4 KB RX cluster (IWM_RBUF_SIZE, if_iwmvar.h:290).
It then dereferences pkt->data + sizeof(*rx_res) + len (:3222) and sets
m->m_len = len (:3251), so ieee80211_input reads up to ~60 KB past the
cluster. iwm_rx_mpdu_mq (:3342-3349) is the same bug. Two memcpys
(iwm_rx_rx_phy_cmd:3102, iwm_handle_rx_statistics:3149) also lack the
payload-length check. The DTS_MEASUREMENT_NOTIFICATION handler at :5535
has the correct check β proving the omissions are oversights.
Why it does not trigger here
No WiFi adapter on the QEMU/KVM guest (ifconfig -l = vtnet0 lo0); iwm
is not in X86_64_GENERIC. Latent on Intel Wireless 7260/8000/9000/9260 HW.
What was validated
- Source trace confirmed (see VERDICT.md
kernel_refs). - Baseline
if_iwm.ko(incl.if_iwm.o) builds clean under-Werror. - Fix
fix.diffapplies (4 hunks) and patchedif_iwm.orebuilds clean under-Werror(if_iwm.o: 79584 -> 79776 bytes).
Reproduce (compile-validation only)
scp this-folder/fix.diff root@guest:/root/df1145.diff cd /usr/src && patch -p1 --forward < /root/df1145.diff cd sys/dev/netif/iwm && rm -f if_iwm.o && make if_iwm.o # -Werror clean
Fix
fix.diff bounds each firmware length against iwm_rx_packet_payload_len(pkt)
and drops short payloads β mirroring the existing correct check at :5535.
DF-1145 β Unbounded firmware-supplied MPDU length in iwm RX path (if_iwm.c)
Verdict
REPRODUCED (code-level, latent at runtime). Genuine unbounded
firmware-length -> OOB read of the RX cluster confirmed by source trace.
Not triggerable at runtime on this guest (no Intel WiFi HW; iwm not in
GENERIC) -> runtime not_testable. fix.diff validated to
apply + compile under -Werror.
Mechanism (trigger -> primitive -> effect)
- Cluster size: RX frames are received into clusters of
IWM_RBUF_SIZE = 4096(sys/dev/netif/iwm/if_iwmvar.h:290). - Real length available:
iwm_rx_packet_len(pkt)returns the hardware-transferred frame size (pkt->len_n_flags & IWM_FH_RSCSR_FRAME_SIZE_MSK,if_iwmreg.h:6936-6941);iwm_rx_packet_payload_len(pkt)subtracts the cmd header (if_iwmreg.h:6943-6948). These are the trusted bounds. - Bug 1 β
iwm_rx_rx_mpdu(sys/dev/netif/iwm/if_iwm.c:3221-3251): len = le16toh(rx_res->byte_count);(:3221) β firmware-supplied, no check againstpkt->len/IWM_RBUF_SIZE.byte_countis au16, so up to 65535.rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len));(:3222) β dereferences up to ~64 KB past the 4 KB cluster.m->m_data = pkt->data + sizeof(*rx_res); m->m_pkthdr.len = m->m_len = len;(:3250-3251) β the mbuf is then handed toieee80211_input, which readslenbytes past the cluster.- Bug 2 β
iwm_rx_mpdu_mq(if_iwm.c:3342-3349): same pattern,len = le16toh(desc->mpdu_len);(:3342) unbounded, thenm->m_len = len(:3348-3349). - Bug 3 (secondary) β
iwm_rx_rx_phy_cmd(:3102) andiwm_handle_rx_statistics(:3149):memcpy(..., sizeof(fixed-struct))with no payload-length check; if the payload is shorter than the struct, thememcpyreads past the cluster. - Proof the omissions are oversights, not policy: the
IWM_DTS_MEASUREMENT_NOTIFICATIONhandler at:5535does guard withif (iwm_rx_packet_payload_len(pkt) < sizeof(*notif)). The MPDU/phy/stat paths simply omit the same check.
Effect
A malicious or buggy firmware (or a crafted RX notification) delivering an
oversized byte_count/mpdu_len causes the driver to read up to ~60 KB
past the 4 KB RX cluster and feed it to the network stack as an mbuf β a
kernel heap/info-leak OOB read, and a potential follow-on memory-safety
issue in ieee80211_input consumers. (CWE-125.)
Threat model / reachability
- Attacker: buggy/malicious WiFi firmware, or an attacker who can inject
frames the firmware forwards with a crafted
byte_count. Requires an Intel Wireless 7260/8000/9000/9260 adapter (iwm). - On this guest: NOT reachable β no WiFi adapter at all (
ifconfig -l=vtnet0 lo0),iwmnot in GENERIC. Valid hard blocker: runtime-unreachable here, latent on Intel-WiFi-equipped HW.
Exploit chain
None developed β this is a read-only primitive (CWE-125 OOB read), and
additionally it is runtime-unreachable on this guest (no WiFi HW). Per the
Phase-6 rules a pure-read primitive has no escalation chain; the realistic
impact ceiling is kernel heap info-leak (cluster-adjacent kernel memory
disclosed into an mbuf delivered up the net stack) and potential downstream
memory-safety bugs in 802.11 RX consumers parsing the over-length frame. No
exploit.c.
PoC changes
No trigger PoC seeded. This folder adds fix.diff, build.sh, run.sh,
VERDICT.md, manifest.json, env.txt, build.log, README.md.
Recommended fix
Bound each firmware-supplied length against iwm_rx_packet_payload_len(pkt)
and drop the frame if it does not fit; add the missing payload-length guard
to the two memcpy sites. Implemented in fix.diff (4 hunks):
- iwm_rx_rx_mpdu: if (sizeof(*rx_res) + len + sizeof(uint32_t) > iwm_rx_packet_payload_len(pkt)) return false; before the status read.
- iwm_rx_mpdu_mq: if (sizeof(*desc) + len > iwm_rx_packet_payload_len(pkt)) return false; before use.
- iwm_rx_rx_phy_cmd / iwm_handle_rx_statistics: if (iwm_rx_packet_payload_len(pkt) < sizeof(...)) return;.
This mirrors the existing correct DTS_MEASUREMENT_NOTIFICATION check at :5535.
Fix verification
not_testablecompile validated -Werror
module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed+compile. iwm_rx_rx_mpdu firmware byte_count no upper bound -> OOB FCS read + mbuf OOB. iwm not in GENERIC.
No comments yet.