iwn_rx_done: missing upper bound on firmware-reported frame length allows OOB read of RX mbuf / kernel heap
Summary
iwn_rx_done at if_iwn.c:3078/3081: len=le16toh(mpdu->len) or le16toh(stat->len) with only LOWER bound check (len>=sizeof(ieee80211_frame_ack)=10) at :3098. NO upper bound check against IWN_RBUF_SIZE=4096. Line 3084: flags=le32toh(*(uint32_t*)(head+len)) reads up to ~60KB past 4KB RX cluster for len up to 65535. Lines 3156-3157: m_data=head, m_pkthdr.len=m_len=len, fabricates mbuf spanning ~60KB but physically in 4KB cluster. ieee80211_input at :3231 walks len bytes of attacker-influenced-size -> kernel heap info leak or panic. Triggered by firmware bug misreporting len or DMA-injection. Fix: check head+len+4 <= IWN_RBUF_SIZE before FCS read.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1123 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | upper-bound len against IWN_RBUF_SIZE before the FCS read; bump ic_ierrors and return | 1.0 KB | view raw |
| iwn_fix_build.log | build-log | if_iwn.ko rebuilt from patched source (with DF-1122) under -Werror, rc=0 | 21.4 KB | view raw |
| VERDICT.md | verdict | full line-by-line trace + fix rationale | 2.5 KB | β raw |
| README.md | readme | why there is no .c trigger | 1.5 KB | β raw |
| env.txt | environment | uname, cc version, kldstat | 383 B | view 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-1123 β iwn_rx_done: missing upper bound on firmware-reported frame length (OOB read / heap overflow)
This finding has no userspace trigger: the bug is in iwn_rx_done
(sys/dev/netif/iwn/if_iwn.c:3078-3157), the RX-done notification handler run
from the iwn interrupt thread. The frame length len is taken from firmware
(le16toh(mpdu->len) / le16toh(stat->len)) and only lower-bounded
(len >= sizeof(ieee80211_frame_ack), :3098). There is no upper bound
against the 4 KB RX cluster (IWN_RBUF_SIZE = 4096), so a buggy or hostile
firmware/PHY reporting len up to 65535 causes:
:3084flags = le32toh(*(uint32_t *)(head + len))β reads up to ~60 KB past the 4 KB DMA cluster (heap OOB read); and:3156-3157m->m_data = head; m->m_pkthdr.len = m->m_len = lenβ fabricates an mbuf claiming a ~60 KB body inside a 4 KB cluster, soieee80211_input(:3231) walks attacker-influenced-size bytes => heap info leak or panic.
Preconditions on this guest: if_iwn.ko ships in /boot/kernel but is NOT
loaded and there is no Intel Wireless (iwn) hardware in the QEMU guest, so
the driver never attaches and the RX path is dead. The bug is therefore
source-confirmed latent β real in compiled module code, unreachable at
runtime here. See VERDICT.md for the line-by-line trace.
There is no .c trigger file because no syscall drives the path; the trigger is
a firmware/PHY event (a misreported RX length), which cannot be synthesized
without the NIC.
DF-1123 β VERDICT
Verdict
SOURCE-CONFIRMED (real bug), NOT REPRODUCED AT RUNTIME on this guest.
The missing upper bound on the firmware-reported frame length is traced
line-by-line in compiled module source. It does not fire here because the iwn
driver never attaches (no Intel WiFi HW). Dormant code path, not a false
positive (if_iwn.ko ships in /boot/kernel).
Mechanism (source trace)
iwn_rx_done (sys/dev/netif/iwn/if_iwn.c:3036-3157) processes an RX_DONE /
MPDU_RX_DONE firmware notification (RX interrupt path):
- :3080 len = le16toh(mpdu->len); or :3081 len = le16toh(stat->len); β
firmware-reported 16-bit frame length.
- :3084 flags = le32toh(*(uint32_t *)(head + len)); β reads 4 bytes at
head + len. head sits inside data->m's IWN_RBUF_SIZE (4096, if_iwnreg.h:54)
DMA cluster. There is no upper bound on len, so a buggy/hostile firmware
reporting len up to 65535 reads up to ~60 KB past the 4 KB cluster (heap OOB read).
- :3098 the only length check is a lower bound: len < sizeof(struct
ieee80211_frame_ack) (10 bytes) β discards too-short frames but never too-long.
- :3156-3157 m->m_data = head; m->m_pkthdr.len = m->m_len = len; fabricates
an mbuf claiming a ~60 KB body inside a 4 KB cluster, so ieee80211_input
(:3231) walks attacker-influenced-size bytes β heap info leak or panic.
Trigger: a firmware/PHY event misreporting the RX length (firmware bug or DMA injection by a hostile radio). There is no userspace syscall trigger.
Why not reproduced here
No Intel Wireless (iwn) hardware; if_iwn.ko not loaded and never attaches;
the RX path is dead. Cannot be synthesized without the NIC.
Fix
fix.diff adds an explicit upper bound before the FCS read: reject if
len > IWN_RBUF_SIZE or head + len + sizeof(uint32_t) would exceed
mtod(data->m) + IWN_RBUF_SIZE (the cluster end), bumping ic_ierrors and
returning. Matches the finding's proposal (check head+len+4 against the
RX buffer size).
Fix validation (compile)
Applies (git apply --check clean) and compiles: if_iwn.ko rebuilt
from patched source (with DF-1122's fix also applied) under -Werror,
if_iwn.c compiled clean, rc=0. Runtime before/after is not_testable (no
iwn HW; the trigger is a firmware/radio event that cannot be synthesized here).
Exploit chain
None β the primitive is firmware/radio-triggered and unreachable at runtime on this guest; impact ceiling is OOB heap read / fabricated-mbuf info leak or panic on a HW-equipped host.
Fix verification
not_testablecompile validated -Werror
module rebuild rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. iwn_rx_done len no upper bound vs IWN_RBUF_SIZE -> OOB FCS read + fabricated mbuf. No WiFi HW.
No comments yet.