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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3840 Β· 11 files
FileTypeDescriptionSize
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
README.md file
↓ download 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).

VERDICT.md file
↓ download raw

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_testable
baseline no→ patch + rebuild →patched clean

Fix 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).
↓ fix.diffper-fix-DF-3840

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.