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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0581 Β· 6 files
FileTypeDescriptionSize
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
README.md readme bug description and reachability
↓ download 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.

VERDICT.md verdict source-trace confirmation + unreachability note
↓ download raw

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)

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).
  • kldstat on this guest shows no wlan/ath/iwm/etc. modules.
  • QEMU config has no WiFi device.
  • The wlan.ko module 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 adjacent struct ieee80211_node fields. 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

Fix verification

not_testable

compile 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.