ieee80211_recv_bar parses BAR control/sequence fields with no frame-length validation β OOB read of up to 10 bytes on short ctl frames
Summary
ieee80211_recv_bar casts the mbuf to struct ieee80211_frame_bar and reads wh->i_ctl (bytes 16-17), wh->i_seq (18-19) and i_fc[1] with no length check of its own. The only guarantee on every ctl dispatch path is m_pkthdr.len >= sizeof(struct ieee80211_frame_min) = 10 bytes. Contrast the ACTION path, which is fully per-subtype length-verified before dispatch - the equivalent BAR check does not exist anywhere. A 10-19 byte BAR (driver-fed or injected) reads up to 10 bytes past valid mbuf data; tid is masked to 4 bits and rxseq to 12 bits, so no corruption/leak - worst case a boundary page-fault panic. Hardening-class but a real absent bound, stronger than DF-0324 because no caller validates either. Not executed (Low). Fix: reject m_pkthdr.len < sizeof(struct ieee80211_frame_bar) at entry.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3313 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| verdict.json | β | 1.8 KB | view raw |
DF-3313 β ieee80211_recv_bar reads BAR control/sequence fields with no frame-length validation (OOB read on short ctl frames)
File: sys/netproto/802_11/wlan/ieee80211_ht.c (pass 2 audit)
What
ieee80211_recv_bar() (sys/netproto/802_11/wlan/ieee80211_ht.c:977-1050)
casts the mbuf to struct ieee80211_frame_bar and reads wh->i_ctl
(bytes 16-17) and wh->i_seq (bytes 18-19) β plus wh->i_fc[1]
(:1046) β with no length check of its own.
The only length guarantee on the ctl-frame path is the generic
m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min) (10 bytes) at
the top of each vap input routine (ieee80211_sta.c:571,
ieee80211_hostap.c, ieee80211_adhoc.c, ieee80211_wds.c,
ieee80211_mesh.c) before vap->iv_recv_ctl(ni, m, subtype) dispatches
subtype BAR straight to ieee80211_recv_bar (ieee80211_sta.c:926-930,
ieee80211_hostap.c:2248-2254, ieee80211_adhoc.c:949-956). Contrast
the ACTION-frame path, which is fully length-verified per subtype by
ieee80211_parse_action() (ieee80211_input.c:754-803) before
ic->ic_recv_action dispatch β the equivalent BAR check simply does not
exist anywhere.
A BAR ctl frame of 10-19 bytes (perfectly deliverable by drivers that
pass truncated ctl frames up, and by injection/SDR setups) makes the
kernel read up to 10 bytes past the valid mbuf data:
tid = MS(le16toh(wh->i_ctl), IEEE80211_BAR_TID) (:996) and
rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT (:1009).
Impact
OOB read only. tid is masked to 4 bits (0-15 β ni_rx_ampdu[tid]
stays in bounds), rxseq to 12 bits; neither value is reflected back to
the sender, so no info leak and no memory corruption. Worst case is a
page-fault panic if the mbuf data ends at a mapping boundary (rx
clusters normally leave the read inside the cluster). Severity: Low.
Recommended fix
--- a/sys/netproto/802_11/wlan/ieee80211_ht.c
+++ b/sys/netproto/802_11/wlan/ieee80211_ht.c
@@ -993,6 +993,10 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
if (!ieee80211_recv_bar_ena) {
@@ return;
}
wh = mtod(m0, struct ieee80211_frame_bar *);
+ if (m0->m_pkthdr.len < sizeof(*wh)) {
+ vap->iv_stats.is_rx_tooshort++;
+ return;
+ }
/* XXX check basic BAR */
(An m_pullup variant is the belt-and-braces form; the length check is
the security-relevant part.)
PoC status
Not executed (Low severity β OOB read with masked sinks, no leak/corruption;
Phase V not warranted under the conditional-verify rule). The missing
check is provable by inspection: no length validation exists on any
BAR dispatch path (grep-clean), and ieee80211_recv_bar reads through
offset 19 unconditionally.
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['README.md: dispatch-path analysis with path:line for every ctl entry (sta.c:926, hostap.c:2248, adhoc.c:949) vs the action-path contrast (ieee80211_input.c:754-803)']
Verified recommended fix
Reject BAR frames shorter than sizeof(struct ieee80211_frame_bar) at the top of ieee80211_recv_bar (see README.md diff).
Verdict
ieee80211_recv_bar (ieee80211_ht.c:977-1050) reads wh->i_ctl/wh->i_seq through byte offset 19 with no own length check; every vap ctl dispatch path guarantees only sizeof(ieee80211_frame_min)=10 bytes before calling it (contrast: ACTION frames are length-verified per subtype in ieee80211_parse_action before dispatch). A short BAR delivered by a driver/injection is an OOB read of up to 10 bytes past valid mbuf data. Sinks are bit-masked (TID 4 bits, seq 12 bits) so there is no corruption/leak; worst case a boundary page-fault panic. Not executed: Low severity, Phase V conditional rule not met. The absent check is certain from source; manifestation depends on the radio driver delivering short ctl frames.
No comments yet.