ieee80211_media_setup unbounded rate-set merge overflows stack buffer rs_rates[15]: 17 unique rates across 11a/b/g/HALF/QUARTER
Summary
ieee80211_media_setup(:1564-1603) declares struct ieee80211_rateset allrates on stack (rs_rates[IEEE80211_RATE_MAXSIZE=15]). Loop(:1565-1594) collects unique rate values across all supported phy modes with NO check rs_nrates<15 before writing rs_rates[j](:1587) and incrementing(:1588). Union of default ratesets for 11a(8)+11b(4)+11g(12)+HALF(8)+QUARTER(8) = 17 distinct {2,3,4,6,9,11,12,18,22,24,27,36,48,54,72,96,108} > 15 slots. Writes rs_rates[15] and [16] past array -> stack corruption. Follow-on read loop(:1595-1603) OOB-reads same slots. Triggered at vap attach for a/b/g NICs with half/quarter support (e.g. Atheros ath). Requires PRIV_DRIVER (root). Stack canary -> deterministic panic.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0365 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | bounds-check rs_nrates before write/increment | 462 B | view raw |
| VERDICT.md | verdict | source-trace confirmation, latent reason | 3.6 KB | β raw |
| env.txt | environment | guest has no WiFi HW | 302 B | view raw |
| README.md | readme | human reproduce doc | 877 B | β 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-0365 PoC β ieee80211_media_setup unbounded rate-set merge
Status: LATENT β code-level overflow confirmed, NOT reachable on audit guest. Severity: Medium
Bug
sys/netproto/802_11/wlan/ieee80211.c:1564 declares
struct ieee80211_rateset allrates on the stack. The collection loop
(lines 1565β1594) writes allrates.rs_rates[j] and increments
allrates.rs_nrates whenever a new unique rate is seen β with no
check that rs_nrates < IEEE80211_RATE_MAXSIZE (= 15). A NIC
advertising 11a + 11b + 11g + HALF + QUARTER modes produces 17
distinct rate values, overflowing rs_rates[15] and rs_rates[16].
Why not testable here
Same as DF-0352: no WiFi HW. ieee80211_media_setup runs only at
vap-attach, which requires a real WiFi driver.
Files
fix.diffβ bounds-check before writeVERDICT.mdβ full narrativeenv.txt
DF-0365 β ieee80211_media_setup unbounded rate-set merge overflows rs_rates[15]
Verdict
LATENT β code-level buffer overflow confirmed by source trace; NOT reachable on this guest (no WiFi hardware).
The loop in ieee80211_media_setup collects unique legacy rates into
struct ieee80211_rateset allrates whose rs_rates[] is exactly
IEEE80211_RATE_MAXSIZE = 15 bytes (sys/netproto/802_11/_ieee80211.h:375),
but the loop has no check that allrates.rs_nrates < 15 before
writing rs_rates[j] and incrementing rs_nrates. With all of
11a + 11b + 11g + HALF + QUARTER modes advertised by the same NIC
(ic_modecaps bits), the union of distinct rates is 17 values β
overflowing the 15-slot array by 2 bytes (stack write past
rs_rates[14]), and the follow-on loop at 1595β1603 OOB-reads the
same slots.
Bug mechanism (source trace)
File: sys/netproto/802_11/wlan/ieee80211.c, function
ieee80211_media_setup (around line 1564).
1564: memset(&allrates, 0, sizeof(allrates));
1565: for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
1566: if (isclr(ic->ic_modecaps, mode))
1567: continue;
1568: addmedia(media, caps, addsta, mode, IFM_AUTO);
...
1571: rs = &ic->ic_sup_rates[mode];
1572: for (i = 0; i < rs->rs_nrates; i++) {
...
1581: r = rate & IEEE80211_RATE_VAL;
1582: for (j = 0; j < allrates.rs_nrates; j++)
1583: if (allrates.rs_rates[j] == r)
1584: break;
1585: if (j == allrates.rs_nrates) {
1586: /* unique, add to the set */
1587: allrates.rs_rates[j] = r; // <-- j can be 15 or 16
1588: allrates.rs_nrates++; // <-- and beyond, no bound
1589: }
...
1594: }
1595: for (i = 0; i < allrates.rs_nrates; i++) { // OOB read
...
1603: }
The header (_ieee80211.h:375):
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
struct ieee80211_rateset {
u_int8_t rs_nrates;
u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
Distinct-rate math (matches finding)
- 11a: {6,9,12,18,24,36,48,54} = 8 values
- 11b: {2,4,11,22} = 4 values (3 new: 2,4,11,22 β 22 not in a)
- 11g: union(11a βͺ {2,4,11,22}) β same set as 11a+b
- HALF: {3,4.5,6,9,12,18,24,27,36} in 0.5 Mb units β adds 27 (=54/2) and may add others
- QUARTER:{1.5,2.25,3,4.5,6,9,12,13.5,18,27} β adds more Union = 17 distinct values > 15 slots β 2-byte stack overflow.
Effects if reachable
- Stack buffer overflow at vap-attach time.
- Triggerable by
PRIV_DRIVER(root) when configuring a multi-mode NIC. - Stack-protector canary β deterministic panic (no info-leak / no controlled RIP from this alone β but corruption of stack frames above allrates could theoretically be exploited if the canary is bypassed).
Why not testable on this guest
Same as DF-0352: the audit guest has no WiFi hardware and no
wlan module loaded. ieee80211_media_setup runs only at
ieee80211_vap_attach time, which requires a real WiFi driver
instance. There is no way for an unprivileged user (or even root
without WiFi HW) to exercise this path on the guest.
Recommended fix
Add a bounds check in the rate-collection loop:
if (j == allrates.rs_nrates) {
if (allrates.rs_nrates >= IEEE80211_RATE_MAXSIZE)
break; /* or continue; */
allrates.rs_rates[j] = r;
allrates.rs_nrates++;
}
See fix.diff for the git-apply-able version.
Files in this folder
fix.diffβ bounds check before write/incrementVERDICT.mdβ this filemanifest.jsonenv.txt
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. ieee80211_media_setup rate-set merge no bounds vs IEEE80211_RATE_MAXSIZE=15 -> 2B stack overflow. No WiFi HW.
No comments yet.