git: 3dc7a1897e0b - main - net80211: correct input_sta length checks and control frame handling

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Fri, 22 Oct 2021 10:45:44 UTC
The branch main has been updated by bz:

URL: https://cgit.FreeBSD.org/src/commit/?id=3dc7a1897e0bb9e4b529c01cb3f88e1c387af5e8

commit 3dc7a1897e0bb9e4b529c01cb3f88e1c387af5e8
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2021-09-30 16:41:19 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2021-10-22 10:42:06 +0000

    net80211: correct input_sta length checks and control frame handling
    
    Correct input_sta "assertion" checks.  CTS/ACK CTRL frames are shorter
    then sizeof(struct ieee80211_frame_min) and were thus running into the
    is_rx_tooshort error case.
    Use ieee80211_anyhdrsize() to handle this better but make sure we do
    at least have the first 2 octets needed for that.
    While here move the safety checks before any code which may not obey
    them later, just for good style.
    
    The non-scanning check further down assumes a frame format also not
    matching control frames.  For now skip the checks for control frames
    which allows us to deal with some of them at least now.
    
    Sponsored by:   The FreeBSD Foundation
    Obtained from:  20210906 wireless v0.91 code drop
    MFC after:      3 days
    Reviewed by:    adrian
    Differential Revision: https://reviews.freebsd.org/D32238
---
 sys/net80211/ieee80211_sta.c | 66 +++++++++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 29 deletions(-)

diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
index cd62266ab942..7ea6187332b1 100644
--- a/sys/net80211/ieee80211_sta.c
+++ b/sys/net80211/ieee80211_sta.c
@@ -552,6 +552,35 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
 	int is_hw_decrypted = 0;
 	int has_decrypted = 0;
 
+	KASSERT(ni != NULL, ("%s: null node, mbuf %p", __func__, m));
+
+	/* Early init in case of early error case. */
+	type = -1;
+
+	/*
+	 * Bit of a cheat here, we use a pointer for a 3-address
+	 * frame format but don't reference fields past outside
+	 * ieee80211_frame_min (or other shorter frames) w/o first
+	 * validating the data is present.
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+
+	if (m->m_pkthdr.len < 2 || m->m_pkthdr.len < ieee80211_anyhdrsize(wh)) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL,
+		    "too short (1): len %u", m->m_pkthdr.len);
+		vap->iv_stats.is_rx_tooshort++;
+		goto err;
+	}
+	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+	    IEEE80211_FC0_VERSION_0) {
+		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
+		    wh->i_fc[0], wh->i_fc[1]);
+		vap->iv_stats.is_rx_badversion++;
+		goto err;
+	}
+
 	/*
 	 * Some devices do hardware decryption all the way through
 	 * to pretending the frame wasn't encrypted in the first place.
@@ -569,7 +598,6 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
 		 * with the M_AMPDU_MPDU flag and we can bypass most of
 		 * the normal processing.
 		 */
-		wh = mtod(m, struct ieee80211_frame *);
 		type = IEEE80211_FC0_TYPE_DATA;
 		dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
 		subtype = IEEE80211_FC0_SUBTYPE_QOS;
@@ -577,39 +605,19 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m,
 		goto resubmit_ampdu;
 	}
 
-	KASSERT(ni != NULL, ("null node"));
 	ni->ni_inact = ni->ni_inact_reload;
 
-	type = -1;			/* undefined */
-
-	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
-		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
-		    ni->ni_macaddr, NULL,
-		    "too short (1): len %u", m->m_pkthdr.len);
-		vap->iv_stats.is_rx_tooshort++;
-		goto out;
-	}
-	/*
-	 * Bit of a cheat here, we use a pointer for a 3-address
-	 * frame format but don't reference fields past outside
-	 * ieee80211_frame_min w/o first validating the data is
-	 * present.
-	 */
-	wh = mtod(m, struct ieee80211_frame *);
-
-	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
-	    IEEE80211_FC0_VERSION_0) {
-		IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
-		    ni->ni_macaddr, NULL, "wrong version, fc %02x:%02x",
-		    wh->i_fc[0], wh->i_fc[1]);
-		vap->iv_stats.is_rx_badversion++;
-		goto err;
-	}
-
 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+	/*
+	 * Control frames are not folowing the header scheme of data and mgmt
+	 * frames so we do not apply extra checks here.
+	 * We probably should do checks on RA (+TA) where available for those
+	 * too, but for now do not drop them.
+	 */
+	if (type != IEEE80211_FC0_TYPE_CTL &&
+	    (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
 		bssid = wh->i_addr2;
 		if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
 			/* not interested in */