=== DF-0640: ieee80211_crypto_decap minlen ignores hdrlen === NOTE: Requires WiFi hardware — bug confirmed by source trace. Harness demonstrates the minlen bypass and double-free path. IEEE80211_WEP_MINLEN (buggy) = 32 (hardcodes sizeof(ieee80211_frame)=24) [Test 1] QoS frame (hdrlen=26, len=34): crypto_decap(hdrlen=26, pkthdr.len=34): minlen check: pkthdr.len=34 >= MINLEN=32 -> PASSED m_copydata at offset hdrlen+3 = 29 m_pullup for hdrlen+cip_header = 26 + 8 = 34 [Test 2] 4-addr QoS frame (hdrlen=32, len=33): -> Buggy check passes (33 >= 32), correct would reject (33 < 40) crypto_decap(hdrlen=32, pkthdr.len=33): minlen check: pkthdr.len=33 >= MINLEN=32 -> PASSED [BUG] CORRECT minlen would be 40 (hdrlen=32) -> SHOULD reject but DIDN'T m_copydata at offset hdrlen+3 = 35 m_copydata: OOB! off=35+len=1 > pkthdr.len=33 -> OOB READ / PANIC! -> IMPACT 1: OOB read -> kernel panic [Test 3] QoS frame (hdrlen=26, len=33): -> Buggy check passes (33 >= 32), correct would reject (33 < 34) crypto_decap(hdrlen=26, pkthdr.len=33): minlen check: pkthdr.len=33 >= MINLEN=32 -> PASSED [BUG] CORRECT minlen would be 34 (hdrlen=26) -> SHOULD reject but DIDN'T m_copydata at offset hdrlen+3 = 29 m_pullup for hdrlen+cip_header = 26 + 8 = 34 m_pullup: fails (pkthdr.len=33 < requested=34) -> FREES mbuf, returns NULL -> m_pullup failed, mbuf FREED, function returns NULL -> CALLER still holds original m -> m_freem(m) -> DOUBLE-FREE! -> IMPACT 2: DOUBLE-FREE (caller will m_freem the freed mbuf) === SUMMARY === hdrlen from ieee80211_hdrspace varies (QoS=26, 4-addr=30/32). Buggy MINLEN uses hardcoded 24, admitting frames too short for ops. Impact 1: m_copydata OOB -> panic (INVARIANTS) or NULL deref Impact 2: m_pullup fail -> mbuf freed -> caller double-free (mbuf double-free is a well-known code-exec primitive) Fix: use hdrlen in the minlen check. RUN_EXIT=0