DragonFlyBSD Kernel Audit
DF-0594 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c
index 1111111..2222222 100644
--- a/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c
+++ b/sys/netproto/802_11/wlan_tkip/ieee80211_crypto_tkip.c
@@ -276,6 +276,24 @@
 	 */
 	wh = mtod(m, struct ieee80211_frame *);
 	ivp = mtod(m, uint8_t *) + hdrlen;
+	/*
+	 * Reject frames too short to hold the 802.11 header + the cipher's
+	 * IV/EIV header + the ICV trailer.  Without this, the length
+	 * arithmetic in tkip_decrypt() underflows: m->m_pkthdr.len (int) minus
+	 * (hdrlen + ic_header + ic_trailer) (u_int) wraps to ~0 and
+	 * wep_decrypt() walks past the mbuf chain (KASSERT panic on INVARIANTS
+	 * kernels, OOB read on production kernels).  The upper-layer
+	 * ieee80211_crypto_decap() only enforces the WEP 32-byte floor, which
+	 * is too small for TKIP (needs >= hdrlen + ic_header + ic_trailer).
+	 */
+	if (m->m_pkthdr.len < hdrlen + (int)(tkip.ic_header + tkip.ic_trailer)) {
+		vap->iv_stats.is_rx_tkipformat++;
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+		    "TKIP", "frame too short for decrypt: len %d, need %d",
+		    m->m_pkthdr.len,
+		    hdrlen + (int)(tkip.ic_header + tkip.ic_trailer));
+		return 0;
+	}
 	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
 		/*
 		 * No extended IV; discard frame.
@@ -353,7 +371,23 @@
 
 		vap->iv_stats.is_crypto_tkipdemic++;
 
-		michael_mic(ctx, k->wk_rxmic, 
+		/*
+		 * Reject frames too short to hold the 802.11 header + the MIC.
+		 * Without this the length arithmetic below underflows: the
+		 * subtraction m->m_pkthdr.len (int) - (hdrlen + ic_miclen)
+		 * (u_int) wraps and michael_mic()/m_copydata() walk past the
+		 * mbuf chain.  Reachable in the HW-decrypt + SW-MIC config.
+		 */
+		if (m->m_pkthdr.len < hdrlen + (int)tkip.ic_miclen) {
+			vap->iv_stats.is_rx_tkipformat++;
+			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_CRYPTO,
+			    wh->i_addr2, "TKIP",
+			    "frame too short for MIC: len %d, need %d",
+			    m->m_pkthdr.len, hdrlen + (int)tkip.ic_miclen);
+			return 0;
+		}
+
+		michael_mic(ctx, k->wk_rxmic,
 			m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
 			mic);
 		m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen,