ieee80211_setup_rates trusts attacker-controlled IE length byte: latent heap overflow of rs_rates[15]
Summary
ieee80211_setup_rates(:420-421): rs->rs_nrates=rates[1]; memcpy(rs->rs_rates,rates+2,rs->rs_nrates). rates[1] is 8-bit attacker-controlled TLV length 0-255. struct ieee80211_rateset.rs_rates is IEEE80211_RATE_MAXSIZE=15 bytes(_ieee80211.h:375-380). RATES IE length>15 -> up to 240 bytes past rs_rates overflow adjacent ieee80211_node fields. xrates handling(:427-436) IS bounded(clamps to MAXSIZE). Currently mitigated by callers(IEEE80211_VERIFY_ELEMENT + parse_beacon!=0 check) but function has NO self-validation. Any future caller regression -> heap overflow of live node corrupting function pointers/locks/creds. Fix: rs->rs_nrates=min(rates[1],IEEE80211_RATE_MAXSIZE).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0581 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | bound nrates to IEEE80211_RATE_MAXSIZE in setup_rates | 746 B | view raw |
| env.txt | environment | uname (no WiFi modules loaded) | 308 B | view raw |
| VERDICT.md | verdict | source-trace confirmation + unreachability note | 4.1 KB | β raw |
| README.md | readme | bug description and reachability | 2.6 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-0581 β ieee80211_setup_rates trusts attacker-controlled IE length
Bug
ieee80211_setup_rates (sys/netproto/802_11/wlan/ieee80211_input.c:413)
copies a TLV rateset from a received management frame into a fixed-size
struct:
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
rs->rs_nrates = rates[1]; /* line 420 -- attacker-controlled */
memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); /* line 421 */
struct ieee80211_rateset (sys/netproto/802_11/_ieee80211.h:375-380):
#define IEEE80211_RATE_MAXSIZE 15
struct ieee80211_rateset {
uint8_t rs_nrates;
uint8_t rs_rates[IEEE80211_RATE_MAXSIZE]; /* 15 bytes */
};
rates[1] is the IE length byte (0..255), and the function trusts it
without bounds check. A rateset IE with length > 15 (legitimate max)
causes the memcpy to write up to 240 bytes past rs_rates[15],
overflowing into whatever struct ieee80211_node fields follow
ni_rates.
The xrates handling at lines 427-436 IS correctly bounded (clamps to IEEE80211_RATE_MAXSIZE).
Mitigations (in callers)
All beacon/probe-response parsing callers go through
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, action)
(sys/netproto/802_11/ieee80211_input.h:31) BEFORE calling
ieee80211_setup_rates. So on production paths, the rates IE is
already length-validated.
However:
- ieee80211_node.c:856 (the ieee80211_setup_rates call from the
neighbor join) takes data from the scan cache, which on INVARIANTS
kernels is KASSERT-checked at ieee80211_scan_sta.c:285-287, but
on production kernels the KASSERT is a no-op and bad data flows
through.
- The function itself has NO bounds check, so any new caller (or a
regression in the existing KASSERT) re-opens the OOB write.
- On default GENERIC (#0 baseline, INVARIANTS ON), the
KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE, ...) at
ieee80211_scan_sta.c:285 fires FIRST and panics the kernel β making
this bug a DoS via panic on default kernels, and an OOB write on
production/no-INVARIANTS kernels.
Reachability on this guest
Requires a WiFi interface in monitor/managed/hostap mode receiving
frames. The guest has NO WiFi hardware (no wlan/ath/iwm/etc.
drivers loaded; QEMU has no WiFi device). Therefore the bug cannot be
triggered at runtime on this guest.
The bug is real and traced line-by-line into source. It is a latent defect that manifests on any DFly system with a WiFi interface under adversarial RF input.
Fix
Bound the length at the function itself (defense in depth, since the function is the actual sink). See fix.diff.
DF-0581 β Verdict: NOT TESTABLE (no WiFi hardware); bug confirmed in source
Verdict
CONFIRMED by source trace; NOT TESTABLE at runtime on this guest (no WiFi hardware; no virtual WiFi device in QEMU).
Bug confirmation (source-only)
- sys/netproto/802_11/wlan/ieee80211_input.c:420 β
rs->rs_nrates = rates[1](attacker-controlled IE length byte, 0..255). - sys/netproto/802_11/wlan/ieee80211_input.c:421 β
memcpy(rs->rs_rates, rates + 2, rs->rs_nrates)β copies up to 255 bytes. - sys/netproto/802_11/_ieee80211.h:375 β
IEEE80211_RATE_MAXSIZE = 15 - sys/netproto/802_11/_ieee80211.h:378-379 β
rs_rates[15]β only 15 bytes of destination. - Overflow: 240 bytes past
rs_rates[15]into adjacentstruct ieee80211_nodefields. - sys/netproto/802_11/wlan/ieee80211_input.c:427-436 β xrates handling IS correctly bounded; only the initial rates copy is buggy.
Caller mitigation:
- sys/netproto/802_11/ieee80211_input.h:31 β IEEE80211_VERIFY_ELEMENT
macro bounds-checks IE length BEFORE setup_rates is called on beacon/
probe response parse paths.
- sys/netproto/802_11/wlan/ieee80211_input.c:667 β beacon parse uses
IEEE80211_VERIFY_ELEMENT on rates.
- sys/netproto/802_11/wlan/ieee80211_hostap.c:1830 β hostap auth uses
IEEE80211_VERIFY_ELEMENT.
- sys/netproto/802_11/wlan/ieee80211_sta.c:1672 β sta assoc resp uses
IEEE80211_VERIFY_ELEMENT.
- sys/netproto/802_11/wlan/ieee80211_mesh.c:2070 β mesh path uses
IEEE80211_VERIFY_ELEMENT.
But the scan-cache path at sys/netproto/802_11/wlan/ieee80211_node.c:856
does NOT explicitly re-verify; it relies on the upstream
KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE, ...) at
ieee80211_scan_sta.c:285. On default GENERIC (INVARIANTS ON) that
KASSERT panics the kernel as a DoS; on production kernels the data
flows through to ieee80211_setup_rates and OOB-writes ni_rates.
The reviewer's analysis is correct: the function itself is unsafe and the bounds check belongs in the function (the actual sink), not just in callers. The xrates code path proves the maintainers know how to do the check correctly; the rates copy is an inconsistency.
Trigger unreachability on this guest
- Requires a WiFi interface (wlan module + radio driver).
kldstaton this guest shows no wlan/ath/iwm/etc. modules.- QEMU config has no WiFi device.
- The
wlan.komodule exists in /boot/kernel but isn't loaded; even if loaded it requires an underlying radio driver.
Therefore: latent bug. Concrete next steps for a maintainer wanting
to exercise it: load wlan.ko + a virtual/simulated WiFi driver
that injects frames, or use an actual USB WiFi dongle in
monitor/hostap mode receiving a crafted beacon with rates[1]=255.
Impact ceiling
- Default GENERIC (INVARIANTS ON): DoS via KASSERT panic at ieee80211_scan_sta.c:285 (panic before setup_rates is called).
- Production (INVARIANTS OFF): OOB write of up to 240 bytes
past
rs_rates[15], corrupting adjacentstruct ieee80211_nodefields. Could in principle be turned into RCE on a kernel without SMEP/SMAP (this guest) IF the corrupted fields are sensitive (function pointers, refcounts) β but reachability is gated on WiFi hardware which is absent here, so the chain is not demonstrated.
Fix validation
Not testable at runtime (no WiFi). Validated by:
1. Source line-by-line trace (above).
2. fix.diff applies cleanly with patch -p1 --dry-run against
sys/netproto/802_11/wlan/ieee80211_input.c.
Kernel references
- sys/netproto/802_11/wlan/ieee80211_input.c:420 β buggy: rs_nrates = rates[1]
- sys/netproto/802_11/wlan/ieee80211_input.c:421 β buggy: unbounded memcpy
- sys/netproto/802_11/wlan/ieee80211_input.c:427-436 β xrates: correctly bounded (reference)
- sys/netproto/802_11/_ieee80211.h:375 β IEEE80211_RATE_MAXSIZE = 15
- sys/netproto/802_11/_ieee80211.h:378-379 β rs_rates[15]
- sys/netproto/802_11/ieee80211_input.h:31 β IEEE80211_VERIFY_ELEMENT macro
- sys/netproto/802_11/wlan/ieee80211_node.c:856 β call site (no explicit pre-check)
- sys/netproto/802_11/wlan/ieee80211_scan_sta.c:285 β KASSERT mitigation (INVARIANTS only)
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. ieee80211_setup_rates rs_nrates=rates[1] no clamp vs [15] -> 240B overflow. No WiFi HW.
No comments yet.