diff options
author | avos <avos@FreeBSD.org> | 2016-06-20 22:39:32 +0000 |
---|---|---|
committer | avos <avos@FreeBSD.org> | 2016-06-20 22:39:32 +0000 |
commit | 39bab70fa61a7bb48f204c2cacbe2f14c50f942f (patch) | |
tree | 3ae87ae3fd4f95236cd12b7fd7d13a9c27a26c29 | |
parent | 2fdd277a83f97fdad90c486e5e0babaf2b9b8d54 (diff) | |
download | FreeBSD-src-39bab70fa61a7bb48f204c2cacbe2f14c50f942f.zip FreeBSD-src-39bab70fa61a7bb48f204c2cacbe2f14c50f942f.tar.gz |
urtwn: fix panic on device detach.
Remove frames from active/pending Tx queues and free related node
references when vap is destroyed to prevent various use-after-free
scenarios.
Reported and tested by: Aleksander Alekseev <afiskon@devzen.ru>
PR: 208632
Approved by: re (gjb)
-rw-r--r-- | sys/dev/urtwn/if_urtwn.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/sys/dev/urtwn/if_urtwn.c b/sys/dev/urtwn/if_urtwn.c index 999bb6e..6ca31fa 100644 --- a/sys/dev/urtwn/if_urtwn.c +++ b/sys/dev/urtwn/if_urtwn.c @@ -208,6 +208,10 @@ static struct ieee80211vap *urtwn_vap_create(struct ieee80211com *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void urtwn_vap_delete(struct ieee80211vap *); +static void urtwn_vap_clear_tx(struct urtwn_softc *, + struct ieee80211vap *); +static void urtwn_vap_clear_tx_queue(struct urtwn_softc *, + urtwn_datahead *, struct ieee80211vap *); static struct mbuf * urtwn_rx_copy_to_mbuf(struct urtwn_softc *, struct r92c_rx_stat *, int); static struct mbuf * urtwn_report_intr(struct usb_xfer *, @@ -824,8 +828,16 @@ urtwn_vap_delete(struct ieee80211vap *vap) struct urtwn_softc *sc = ic->ic_softc; struct urtwn_vap *uvp = URTWN_VAP(vap); + /* Guarantee that nothing will go through this vap. */ + ieee80211_new_state(vap, IEEE80211_S_INIT, -1); + ieee80211_draintask(ic, &vap->iv_nstate_task); + + URTWN_LOCK(sc); if (uvp->bcn_mbuf != NULL) m_freem(uvp->bcn_mbuf); + /* Cancel any unfinished Tx. */ + urtwn_vap_clear_tx(sc, vap); + URTWN_UNLOCK(sc); if (vap->iv_opmode == IEEE80211_M_IBSS) ieee80211_draintask(ic, &uvp->tsf_task_adhoc); if (URTWN_CHIP_HAS_RATECTL(sc)) @@ -834,6 +846,41 @@ urtwn_vap_delete(struct ieee80211vap *vap) free(uvp, M_80211_VAP); } +static void +urtwn_vap_clear_tx(struct urtwn_softc *sc, struct ieee80211vap *vap) +{ + + URTWN_ASSERT_LOCKED(sc); + + urtwn_vap_clear_tx_queue(sc, &sc->sc_tx_active, vap); + urtwn_vap_clear_tx_queue(sc, &sc->sc_tx_pending, vap); +} + +static void +urtwn_vap_clear_tx_queue(struct urtwn_softc *sc, urtwn_datahead *head, + struct ieee80211vap *vap) +{ + struct urtwn_data *dp, *tmp; + + STAILQ_FOREACH_SAFE(dp, head, next, tmp) { + if (dp->ni != NULL) { + if (dp->ni->ni_vap == vap) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + + STAILQ_REMOVE(head, dp, urtwn_data, next); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, dp, + next); + } + } + } +} + static struct mbuf * urtwn_rx_copy_to_mbuf(struct urtwn_softc *sc, struct r92c_rx_stat *stat, int totlen) |