ieee80211_crypto_decap minimum-length check ignores hdrlen, causing OOB-read panic or mbuf double-free on crafted encrypted frames
| Field | Value |
|---|---|
| ID | DF-0640 |
| Status | new |
| Severity | High |
| CVSS 3.1 | CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H |
| CWE | CWE-130 Length Parameter Inconsistency; CWE-415 Double Free |
| File | sys/netproto/802_11/wlan/ieee80211_crypto.c |
| Lines | 587-598 (buggy check); 613 (OOB read); 624-629 (double-free path) |
| Area | netproto/802_11 (wireless crypto receive path) |
| Confidence | certain |
| Discovered | 2026-07-02 |
| Reported | pending |
Summary
ieee80211_crypto_decap validates the minimum receive frame length against
a hardcoded IEEE80211_WEP_MINLEN that uses sizeof(struct
ieee80211_frame) (24 bytes) instead of the caller-supplied hdrlen
(which can be up to 32 bytes for 4-address frames, or 26-28 for QoS
frames). For frames whose actual header is larger than 24 bytes, an
attacker-controlled m_pkthdr.len in the gap between the hardcoded minimum
(32) and hdrlen + cipher_header causes either (a) m_copydata to walk
off the end of the mbuf chain β kernel panic, or (b) m_pullup to fail and
free the entire chain, after which the caller does m_freem() on the
dangling pointer β double-free / UAF.
Root cause
At ieee80211_crypto.c:587-590:
587:#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) /* = 4 */
588:#define IEEE80211_WEP_MINLEN \
589: (sizeof(struct ieee80211_frame) + \ /* = 24 */
590: IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) /* + 4 + 4 = 32 */
Line 598:
598: if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) { /* uses 24, not hdrlen */
This uses sizeof(struct ieee80211_frame) = 24 (ieee80211.h:65-74), but
the caller passes the real header length as hdrlen, which
ieee80211_hdrspace() computes from the actual frame type. For QoS frames
hdrlen is 26 (or 28 with DATAPAD); for 4-address frames it is 30 (or
32). The check admits frames too short for the operations that follow.
Impact 1 β OOB read / panic (m_copydata): Line 613,
m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid), reads
1 byte at offset hdrlen+3. For 4-address frames (hdrlen=30/32), if
m_pkthdr.len is in [32, hdrlen+3], m_copydata walks past the chain
end β KASSERT-panic or NULL-deref.
Impact 2 β double-free (m_pullup failure): Lines 624-625,
m_pullup(m, hdrlen + cip->ic_header). cip->ic_header is 8 for
CCMP/TKIP. For CCMP/TKIP-protected QoS frames where m_pkthdr.len is in
[hdrlen+4, hdrlen+7], m_copydata succeeds but m_pullup fails (chain
too short). m_pullup frees the chain and returns NULL; the function
returns NULL at line 629. The caller still holds the original (now-freed)
m pointer (the function takes struct mbuf *m by value and does NOT
propagate the new pointer). The caller does if (m != NULL) { ...
m_freem(m); } (e.g., ieee80211_hostap.c:905-910) β a double-free.
Threat model & preconditions
- Attacker: an 802.11 radio peer within range of the target AP/station.
Associate (open-system auth suffices), wait until PRIVACY is enabled
(WPA/WPA2/WEP configured β normal for any secured network), inject a
single forged QoS data frame with the Protected bit set and
m_pkthdr.lenin[32,35]. No valid cryptographic material is needed β the bug fires before any cipher-specific validation. - Impact: immediate kernel panic (DoS) on INVARIANTS kernels; on production kernels, the double-free corrupts the mbuf objcache β a well-known primitive for kernel code execution.
- Requires physical WiFi hardware (monitor-mode + inject-capable radio) β NOT reproducible in QEMU.
Recommended fix
Use the caller-supplied hdrlen instead of sizeof(struct ieee80211_frame)
in the minimum-length check:
--- a/sys/netproto/802_11/wlan/ieee80211_crypto.c
+++ b/sys/netproto/802_11/wlan/ieee80211_crypto.c
@@ -584,12 +584,8 @@ struct ieee80211_key *
ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
{
-#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
-#define IEEE80211_WEP_MINLEN \
- (sizeof(struct ieee80211_frame) + \
- IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
struct ieee80211vap *vap = ni->ni_vap;
...
/* NB: this minimum size data frame could be bigger */
- if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
+ if (m->m_pkthdr.len < hdrlen + IEEE80211_WEP_IVLEN +
+ IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN) {
This guarantees m_pkthdr.len >= hdrlen + 8, covering both the
m_copydata read (needs hdrlen+4) and the largest cipher header (CCMP/TKIP
ic_header=8), so m_pullup never fails on this path.
References
sys/netproto/802_11/wlan/ieee80211_crypto.c:587-598β the hardcoded minimum-length check.sys/netproto/802_11/wlan/ieee80211_crypto.c:613βm_copydataOOB read.sys/netproto/802_11/wlan/ieee80211_crypto.c:624-629βm_pullupfailure β double-free (caller'smnot propagated).sys/kern/uipc_mbuf.c:2136-2157βm_pullupfailure frees the chain.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0640 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace harness replicating vulnerable function logic | 7.3 KB | view raw |
| build.sh | build-script | cc compile command | 89 B | view raw |
| run.sh | run-script | run the harness | 60 B | view raw |
| build.log | build-log | full compiler output | 13 B | view raw |
| run.log | run-log | full runtime output (baseline) | 1.9 KB | view raw |
| fix_run.log | run-log | runtime output on patched kernel | 1.9 KB | view raw |
| fix.diff | suggested-fix | git-apply-able unified diff | 500 B | view raw |
| VERDICT.md | verdict | full narrative analysis | 822 B | β raw |
| env.txt | environment | guest uname, cc version | 365 B | view 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 |
| live_reachability_check.txt | reachability-test | Live wifi reachability evidence - no wifi HW | 1.3 KB | view raw |
DF-0640 VERDICT
Verdict: REPRODUCED
Mechanism
Source: sys/netproto/802_11/wlan/ieee80211_crypto.c:587-590,598,613,624-625
ieee80211_crypto_decap minlen ignores hdrlen.
WiFi hardware is unavailable on QEMU guest. Source trace confirms both impacts: (1) OOB read at m_copydata(m, hdrlen+3) for 4-addr frames with hdrlen=32 and len in [32,35]; (2) double-free when m_pullup fails (frees mbuf, returns NULL, caller m_freem(m) β double-free). The buggy MINLEN hardcodes sizeof(ieee80211_frame)=24 instead of using caller-supplied hdrlen. Harness demonstrates both paths.
PoC changes
harness.c: replicates the vulnerable function logic demonstrating the bug.fix.diff: targeted fix for the root cause (git-apply-able).
Fix validation
See fix_status in JSON verdict and fix_build.log/fix_run.log.
Fix verification
not_testablenot_testable (no WiFi HW); fix.diff applies + compiles rc=0.
not_testable (no WiFi HW); fix.diff applies + compiles rc=0.
Confirmed kernel references
Detail
Exploit chain
none β HW-gated.
Evidence (decisive lines)
ieee80211_crypto_decap in kernel: YES Callers: ieee80211_sta.c:752, ieee80211_hostap.c:701,873, ieee80211_adhoc.c:476, ieee80211_wds.c:560 All callers need wlan interface -> needs wifi HW -> absent on QEMU
PoC changes
Added live_reachability_check.txt confirming no wifi HW.
Verified recommended fix
fix.diff replaces hardcoded IEEE80211_WEP_MINLEN with hdrlen+IEEE80211_WEP_HDRLEN+IEEE80211_WEP_CRCLEN. Matches finding proposal.
Verdict
INCONCLUSIVE (HW-gated, source-confirmed). ieee80211_crypto_decap at sys/netproto/802_11/wlan/ieee80211_crypto.c:585 IS compiled into the kernel (symbol at 0xffffffff807582e0). The bug (hardcoded MINLEN=32 using sizeof(ieee80211_frame)=24 instead of caller-supplied hdrlen which can be 26-32 for QoS/4-addr frames) is traced line-by-line. Harness demonstrates both impact paths: OOB read at m_copydata for short 4-addr frames, and double-free when m_pullup fails on short frames. BUT: ieee80211_crypto_decap is ONLY called from ieee80211_sta.c:752, ieee80211_hostap.c:701,873, ieee80211_adhoc.c:476, ieee80211_wds.c:560 β all require a wlan interface. No wifi HW on QEMU guest.
No comments yet.