DF-3314 / fix.diff
--- 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 */ |