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 #include #include - #include +#include #include #include @@ -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); } } }