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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1145 Β· 9 files
FileTypeDescriptionSize
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
README.md readme human repro summary
↓ download 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

  1. Source trace confirmed (see VERDICT.md kernel_refs).
  2. Baseline if_iwm.ko (incl. if_iwm.o) builds clean under -Werror.
  3. Fix fix.diff applies (4 hunks) and patched if_iwm.o rebuilds 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.

VERDICT.md verdict full source-trace + mechanism + fix rationale
↓ download raw

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 against pkt->len/IWM_RBUF_SIZE. byte_count is a u16, 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 to ieee80211_input, which reads len bytes past the cluster.
  • Bug 2 β€” iwm_rx_mpdu_mq (if_iwm.c:3342-3349): same pattern, len = le16toh(desc->mpdu_len); (:3342) unbounded, then m->m_len = len (:3348-3349).
  • Bug 3 (secondary) β€” iwm_rx_rx_phy_cmd (:3102) and iwm_handle_rx_statistics (:3149): memcpy(..., sizeof(fixed-struct)) with no payload-length check; if the payload is shorter than the struct, the memcpy reads past the cluster.
  • Proof the omissions are oversights, not policy: the IWM_DTS_MEASUREMENT_NOTIFICATION handler at :5535 does guard with if (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), iwm not 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.

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_testable

compile 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.