DragonFlyBSD Kernel Audit
DF-0320 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netproto/802_11/ieee80211_ht.h b/sys/netproto/802_11/ieee80211_ht.h
--- a/sys/netproto/802_11/ieee80211_ht.h
+++ b/sys/netproto/802_11/ieee80211_ht.h
@@ -159,7 +159,15 @@
 	int		rxa_nframes;	/* frames since ADDBA */
 	struct mbuf *rxa_m[IEEE80211_AGGR_BAWMAX];
 	void		*rxa_private;
-	uint64_t	rxa_pad[3];
+	/*
+	 * Serializes rxa_m[] mutation across the RX reorder path
+	 * (ieee80211_ampdu_reorder), the node-age callout
+	 * (ieee80211_ht_node_age -> ampdu_rx_flush) and ADDBA re-init
+	 * (ampdu_rx_start).  Without this, concurrent RX + ADDBA/age
+	 * leads to double-free / use-after-free of mbufs in rxa_m[].
+	 */
+	struct spinlock	rxa_lock;
+	uint64_t	rxa_pad[2];
 };
 
 void	ieee80211_ht_attach(struct ieee80211com *);

diff --git a/sys/netproto/802_11/wlan/ieee80211_ht.c b/sys/netproto/802_11/wlan/ieee80211_ht.c
--- a/sys/netproto/802_11/wlan/ieee80211_ht.c
+++ b/sys/netproto/802_11/wlan/ieee80211_ht.c
@@ -40,8 +40,8 @@
 #include <sys/malloc.h>
 #include <sys/systm.h> 
 #include <sys/endian.h>
- 
 #include <sys/socket.h>
+#include <sys/spinlock2.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
@@ -548,9 +548,12 @@
 		 * AMPDU previously setup and not terminated with a DELBA,
 		 * flush the reorder q's in case anything remains.
 		 */
+		spin_lock(&rap->rxa_lock);
 		ampdu_rx_purge(rap);
+		spin_unlock(&rap->rxa_lock);
 	}
 	memset(rap, 0, sizeof(*rap));
+	spin_init(&rap->rxa_lock, "ampdu-rx");
 	rap->rxa_wnd = (bufsiz == 0) ?
 	    IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
 	rap->rxa_start = MS(baseqctl, IEEE80211_BASEQ_START);
@@ -891,7 +894,9 @@
 				if (rap->rxa_qframes != 0) {
 					vap->iv_stats.is_ampdu_rx_age +=
 					    rap->rxa_qframes;
+					spin_lock(&rap->rxa_lock);
 					ampdu_rx_flush(ni, rap);
+					spin_unlock(&rap->rxa_lock);
 				}
 				rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
 				return PROCESS;
@@ -1148,7 +1153,9 @@
 			 * frame; flush the reorder buffer.
 			 */
 			vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
+			spin_lock(&rap->rxa_lock);
 			ampdu_rx_flush(ni, rap);
+			spin_unlock(&rap->rxa_lock);
 		}
 	}
 }