1-byte use-after-free read in iwi_frame_intr(): frame->rate read at :1353 from the old rx mbuf after ieee80211_input() consumed/freed it (softled enabled by default)
Summary
iwi_rx_intr passes frame pointing into data->m's cluster; iwi_frame_intr swaps data->m to a fresh mbuf, unlocks, and hands the old mbuf to ieee80211_input/ieee80211_input_all. net80211 consumes the mbuf - drop paths free it synchronously. After re-taking the lock the driver reads frame->rate whenever sc_softled is set (default 1) for a data frame - a 1-byte read of freed (or netisr-pending) mbuf-cluster memory on every softled data-frame receive. Value only indexes the LED blink table - correctness/lifetime defect with negligible observability, hence Low. Snapshot-before-handoff fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3840 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 1022 B | β raw | |
| VERDICT.md | file | 2.2 KB | β raw | |
| build.log | file | 85 B | view raw | |
| build.sh | file | 82 B | view raw | |
| env.txt | file | 286 B | view raw | |
| fix.diff | file | 911 B | view raw | |
| manifest.json | file | 578 B | view raw | |
| run.log | file | 959 B | view raw | |
| run.sh | file | 90 B | view raw | |
| trigger.c | file | 1.7 KB | view raw | |
| verdict.json | file | 2.8 KB | view raw |
DF-3840 β if_iwi(4) 1-byte use-after-free READ in iwi_frame_intr():
frame points into the old rx mbuf; the mbuf is handed to
ieee80211_input() which consumes/frees it (drop paths free synchronously);
afterwards the driver reads frame->rate (if_iwi.c:1353) with softled
enabled (default: sc->sc_softled = 1, if_iwi.c:3622).
WHAT TO LOOK AT trigger.c documentation harness with the exact call chain. VERDICT.md line-by-line trace incl. the net80211 free sites. fix.diff capture frame->rate before the handoff.
BUILD cc -O2 -o trigger trigger.c (compiles clean on the guest) RUN ./trigger (stub: hardware-gated path) EXPECT On hardware (iwi NIC associated, hw.iwi?.softled=1): every received DATA frame that ieee80211_input drops synchronously makes the driver read 1 byte of a freed mbuf cluster. Observable with a watchpoint on the cluster free. Guest: builds only.
FAMILY none β new in pass 2 (DF-1239/1240/1241 are different bugs).
DF-3840 VERDICT β status: inconclusive (hardware-gated), source-level: CERTAIN
Line-by-line trace (sys/dev/netif/iwi/if_iwi.c):
1632-1652 iwi_rx_intr(): hdr = mtod(data->m, struct iwi_hdr );
iwi_frame_intr(sc, data, idx, (struct iwi_frame )(hdr + 1));
-> frame points INTO data->m's mbuf cluster.
1314-1315 iwi_frame_intr(): m = data->m; data->m = mnew; (swap; m is the
old mbuf, frame still points into it)
1324 rssi = frame->rssi_dbm; (safe β before the handoff)
1335 IWI_UNLOCK(sc);
1337-1342 ni = ieee80211_find_rxnode(...); type = ieee80211_input(ni, m, ...);
or ieee80211_input_all(ic, m, ...);
net80211 CONSUMES m. Drop paths free it synchronously:
sys/netproto/802_11/wlan/ieee80211_input.c:169, :211, :245
(m_freem(m)); delivered frames are queued to netisr and freed
shortly after.
1344 IWI_LOCK(sc);
1345 if (sc->sc_softled) { <- default 1 (if_iwi.c:3622)
1352 if (type == IEEE80211_FC0_TYPE_DATA)
1353 sc->sc_rxrate = frame->rate; <-- READ AFTER FREE
Impact: 1-byte read of freed (or about-to-be-freed) mbuf cluster memory, executed on every softled-enabled data-frame receive that input drops. The value only selects an LED blink-rate table index (if_iwi.c:3511-3567) β no direct disclosure path; this is a correctness/lifetime defect (CWE-416) that reads stale/reallocated memory and, in the delivered-frame case, races the netisr free. Severity Low, confidence certain (the code order is unambiguous).
Why not reproduced: guest has no Intel 2200BG/2915ABG NIC (run.log: "iwi_ifaces_rc=1", no iwi in ifconfig -l). The stub compiles clean (build.log).
Fix: fix.diff β snapshot rx_rate = frame->rate before IWI_UNLOCK and use
the snapshot at :1353.
Fix validation: not possible on this guest β fix_status not_applicable.
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 validation impossible on this guest: the vulnerable device (AMD radeon CS / Intel 2200BG) does not exist, so neither baseline nor patched kernel can exercise the path. fix.diff authored against the read-only sys/ tree, never applied.
fix.diff in this pack; structurally mirrors the bounds checks the same driver family already enforces elsewhere (radeon_cs_packet_parse fit check; iwn ring-index masking).
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md (what to look at), VERDICT.md (line-by-line trace), trigger.c/mkbadfw.c, build.log (clean guest compile, gcc 8.3), run.log (honest ENOENT / generator RUN_OK), env.txt (guest has no DRI, no iwi NIC), fix.diff
PoC changes
PoC authored fresh for this finding (cross-file family seed): raw DRM ioctl ABI (cmd low byte 0x66 dispatches RADEON_CS on DF drm_ioctl.c:809), chunk structs and packet encodings verified against sys/dev/drm/include/uapi/drm/radeon_drm.h; iwi mkbadfw.c emits the v3 combined-image header per if_iwireg.h:161-166.
Verified recommended fix
Snapshot rx_rate = frame->rate before the ieee80211_input() handoff and use the snapshot at :1353.
Verdict
Source-level certain, runtime inconclusive: the guest has no Intel 2200BG/2915ABG NIC (run.log: no iwi interface). frame points into the old rx mbuf (:1651-1652), the mbuf is handed to ieee80211_input/ieee80211_input_all (:1337-1342) which frees it synchronously on drop paths (ieee80211_input.c:169/211/245), and after re-taking the lock the driver reads frame->rate (:1353) whenever sc_softled is set (default 1, :3622) and the frame was a data frame. 1-byte stale/freed-mbuf read whose value only indexes the LED blink table; new pass-2 lifetime bug, distinct from DF-1239/1240/1241.
No comments yet.