⬢ DragonFlyBSD Kernel Audit
DF-3314 / fix.diff
← back to finding ↓ download raw
--- a/sys/netproto/802_11/wlan/ieee80211_node.c
+++ b/sys/netproto/802_11/wlan/ieee80211_node.c
@@ -1114,8 +1114,10 @@
 {
 	struct ieee80211com *ic = ni->ni_ic;
 
-	ieee80211_ratectl_node_deinit(ni);
-	ic->ic_node_cleanup(ni);
+	if ((ni->ni_flags & IEEE80211_NODE_VAPGONE) == 0) {
+		ieee80211_ratectl_node_deinit(ni);
+		ic->ic_node_cleanup(ni);
+	}
 	ieee80211_ies_cleanup(&ni->ni_ies);
 	ieee80211_psq_cleanup(&ni->ni_psq);
 	IEEE80211_FREE(ni, M_80211_NODE);
@@ -1930,6 +1932,25 @@
 		 * the references are dropped storage will be
 		 * reclaimed.
 		 */
+		/*
+		 * Do the AID bitmap bookkeeping NOW, while the vap is
+		 * guaranteed to be alive.  A node that outlives its vap
+		 * (e.g. the last reference is held by a driver tx
+		 * completion arriving after vap destroy) must never
+		 * dereference ni_vap again -- but _ieee80211_free_node
+		 * does, via its ni_associd check.  Clearing the bit and
+		 * the field here is safe for all callers: node_leave()
+		 * and ieee80211_node_table_reset() have already done
+		 * AID_CLR (idempotent), and for the sta_join1() bss
+		 * replacement it moves the clear earlier with the same
+		 * net effect.
+		 */
+		if (ni->ni_associd != 0) {
+			struct ieee80211vap *vap = ni->ni_vap;
+			if (vap->iv_aid_bitmap != NULL)
+				IEEE80211_AID_CLR(vap, ni->ni_associd);
+			ni->ni_associd = 0;
+		}
 		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
 		LIST_REMOVE(ni, ni_hash);
 		ni->ni_table = NULL;		/* clear reference */
@@ -1994,6 +2015,20 @@
 				IEEE80211_AID_CLR(vap, ni->ni_associd);
 		}
 		ni->ni_wdsvap = NULL;		/* clear reference */
+		/*
+		 * The vap is going away (or the node's vap is already
+		 * gone when match == NULL at com detach).  Do all
+		 * vap-dependent cleanup NOW while ni_vap is still valid:
+		 * if a driver holds the last reference, the eventual
+		 * ieee80211_free_node() must not touch ni_vap again --
+		 * node_free()'s ratectl/cleanup calls would otherwise
+		 * dereference freed vap memory.
+		 */
+		if ((ni->ni_flags & IEEE80211_NODE_VAPGONE) == 0) {
+			nt->nt_ic->ic_node_cleanup(ni);
+			ieee80211_ratectl_node_deinit(ni);
+			ni->ni_flags |= IEEE80211_NODE_VAPGONE;
+		}
 		node_reclaim(nt, ni);
 	}
 	if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) {
--- a/sys/netproto/802_11/ieee80211_node.h
+++ b/sys/netproto/802_11/ieee80211_node.h
@@ -137,6 +137,7 @@
 #define	IEEE80211_NODE_ASSOCID	0x020000	/* xmit requires associd */
 #define	IEEE80211_NODE_AMSDU_RX	0x040000	/* AMSDU rx enabled */
 #define	IEEE80211_NODE_AMSDU_TX	0x080000	/* AMSDU tx enabled */
+#define	IEEE80211_NODE_VAPGONE	0x100000	/* vap detached; cleanup done */
 	uint16_t		ni_associd;	/* association ID */
 	uint16_t		ni_vlan;	/* vlan tag */
 	uint16_t		ni_txpower;	/* current transmit power */