Download raw body.
qwx use-after-free fix
> Date: Tue, 14 Jul 2026 18:24:24 +0200
> From: Stefan Sperling <stsp@stsp.name>
>
> Memory allocation failures in qwx_vif_alloc() can lead to crashes.
>
> The reason is that the driver will then use a stale vif pointer from
> sc->vif_list from previous down/up cycles. The vif structure contains
> DMA maps used for sending management frames.
>
> Rather than trying to fix our broken management of the vif list,
> the patch at the bottom removes the list in favour of a single vif
> structure, allocates DMA maps at attach time, and keeps them allocated
> across down/up cycles. We keep a list of 8 maps, each of which can map
> a single frame, so this shouldn't be wasteful.
>
> Here is a reproducer diff.
> if you want to reproduce my findings, boot a kernel with it, then
> suspend/resume once, then try to bring the interface up after resume.
> Since this code uses BUS_DMA_NOWAIT the crash triggers by chance on
> a stock kernel if this memory allocation path fails.
Nice. And yes, while working on my "waitok" diff I ran into this bit
of code and couldn't work out the lifetime of the vif structures.
Lightly tested, but suspend/resume and hibernate still work on my
amd64 laptop.
ok kettenis@OB
> CRASH: --- sys/dev/ic/qwx.c
> CRASH: +++ sys/dev/ic/qwx.c
> CRASH: @@ -116,6 +116,8 @@ hweight8(uint8_t x)
> CRASH:
> CRASH: /* #define QWX_DEBUG */
> CRASH:
> CRASH: +static int qwx_resuming;
> CRASH: +
> CRASH: #include <dev/ic/qwxreg.h>
> CRASH: #include <dev/ic/qwxvar.h>
> CRASH:
> CRASH: @@ -24022,8 +24024,13 @@ qwx_vif_alloc(struct qwx_softc *sc)
> CRASH: for (i = 0; i < nitems(txmgmt->data); i++) {
> CRASH: struct qwx_tx_data *tx_data = &txmgmt->data[i];
> CRASH:
> CRASH: - ret = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
> CRASH: - BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &tx_data->map);
> CRASH: + if (qwx_resuming && i > 6) {
> CRASH: + ret = ENOMEM;
> CRASH: + qwx_resuming = 0;
> CRASH: + } else {
> CRASH: + ret = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
> CRASH: + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &tx_data->map);
> CRASH: + }
> CRASH: if (ret) {
> CRASH: qwx_vif_free(sc, arvif);
> CRASH: return NULL;
> CRASH: @@ -27489,6 +27496,7 @@ qwx_activate(struct device *self, int act)
> CRASH: sc->sc_dev.dv_xname);
> CRASH: }
> CRASH: rw_exit(&sc->ioctl_rwl);
> CRASH: + qwx_resuming = 1;
> CRASH: break;
> CRASH: }
> CRASH:
>
>
> Fix:
>
> Replace virtual interface list with a single interface structure
>
> Should fix use of stale pointers on the vif list which caused crashes.
> We do not support multiple interfaces per driver in our net80211 stack,
> so this list inherited from Linux was pointless for us anyway.
>
> ok?
>
> M sys/dev/ic/qwx.c | 59+ 138-
> M sys/dev/ic/qwxvar.h | 1+ 6-
>
> 2 files changed, 60 insertions(+), 144 deletions(-)
>
> commit - 03332418d3c68eaa3a6cced9f13621a1dc17c1f9
> commit + 8b38c9a0ec77696975740fa34e14b8f85f513bbf
> blob - 62d805dffc20300433d2d806d92aa06b3801b63d
> blob + 55106d1812e2c7b3a521b044d36f76c7fc232b84
> --- sys/dev/ic/qwx.c
> +++ sys/dev/ic/qwx.c
> @@ -168,7 +168,6 @@ int qwx_wmi_vdev_install_key(struct qwx_softc *,
> int qwx_dp_peer_rx_pn_replay_config(struct qwx_softc *, struct qwx_vif *,
> struct ieee80211_node *, struct ieee80211_key *, int);
> void qwx_setkey_clear(struct qwx_softc *);
> -void qwx_vif_free_all(struct qwx_softc *);
> void qwx_dp_stop_shadow_timers(struct qwx_softc *);
> void qwx_ce_stop_shadow_timers(struct qwx_softc *);
> int qwx_wmi_vdev_set_param_cmd(struct qwx_softc *, uint32_t, uint8_t,
> @@ -420,6 +419,24 @@ qwx_del_task_all(struct qwx_softc *sc)
> }
>
> void
> +qwx_vif_purge(struct qwx_softc *sc)
> +{
> + struct qwx_vif *arvif = &sc->sc_vif;
> + struct qwx_txmgmt_queue *txmgmt;
> + int i;
> +
> + txmgmt = &arvif->txmgmt;
> + for (i = 0; i < nitems(txmgmt->data); i++) {
> + struct qwx_tx_data *tx_data = &txmgmt->data[i];
> +
> + if (tx_data->m) {
> + m_freem(tx_data->m);
> + tx_data->m = NULL;
> + }
> + }
> +}
> +
> +void
> qwx_stop(struct ifnet *ifp)
> {
> struct qwx_softc *sc = ifp->if_softc;
> @@ -497,7 +514,7 @@ qwx_stop(struct ifnet *ifp)
> /* free some DMA allocations and power off hardware */
> qwx_core_deinit(sc);
>
> - qwx_vif_free_all(sc);
> + qwx_vif_purge(sc);
>
> splx(s);
> }
> @@ -574,7 +591,7 @@ qwx_tx(struct qwx_softc *sc, struct mbuf *m, struct ie
> {
> struct ieee80211com *ic = &sc->sc_ic;
> struct ieee80211_frame *wh;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> uint8_t frame_type;
>
> @@ -884,7 +901,7 @@ qwx_add_sta_key(struct qwx_softc *sc, struct ieee80211
> struct ieee80211com *ic = &sc->sc_ic;
> struct qwx_node *nq = (struct qwx_node *)ni;
> struct ath11k_peer *peer;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> int ret = 0;
> uint32_t flags = 0;
> @@ -949,7 +966,7 @@ qwx_del_sta_key(struct qwx_softc *sc, struct ieee80211
> struct ieee80211_key *k)
> {
> struct qwx_node *nq = (struct qwx_node *)ni;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> int ret = 0;
>
> ret = qwx_wmi_install_key_cmd(sc, arvif, ni->ni_macaddr, k, 0, 1);
> @@ -1008,7 +1025,7 @@ qwx_setkey_task(void *arg)
> void
> qwx_clear_hwkeys(struct qwx_softc *sc, struct ath11k_peer *peer)
> {
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct wmi_vdev_install_key_arg arg = {
> .vdev_id = arvif->vdev_id,
> @@ -13266,7 +13283,6 @@ void
> qwx_scan_event(struct qwx_softc *sc, struct mbuf *m)
> {
> struct wmi_scan_event scan_ev = { 0 };
> - struct qwx_vif *arvif;
>
> if (qwx_pull_scan_ev(sc, m, &scan_ev) != 0) {
> printf("%s: failed to extract scan event",
> @@ -13276,20 +13292,7 @@ qwx_scan_event(struct qwx_softc *sc, struct mbuf *m)
> #ifdef notyet
> rcu_read_lock();
> #endif
> - TAILQ_FOREACH(arvif, &sc->vif_list, entry) {
> - if (arvif->vdev_id == scan_ev.vdev_id)
> - break;
> - }
> -
> - if (!arvif) {
> - printf("%s: received scan event for unknown vdev\n",
> - sc->sc_dev.dv_xname);
> #if 0
> - rcu_read_unlock();
> -#endif
> - return;
> - }
> -#if 0
> spin_lock_bh(&ar->data_lock);
> #endif
> DNPRINTF(QWX_D_WMI,
> @@ -13375,7 +13378,6 @@ qwx_pull_chan_info_ev(struct qwx_softc *sc, uint8_t *e
> void
> qwx_chan_info_event(struct qwx_softc *sc, struct mbuf *m)
> {
> - struct qwx_vif *arvif;
> struct wmi_chan_info_event ch_info_ev = {0};
> struct qwx_survey_info *survey;
> int idx;
> @@ -13403,20 +13405,6 @@ qwx_chan_info_event(struct qwx_softc *sc, struct mbuf
> }
> #ifdef notyet
> rcu_read_lock();
> -#endif
> - TAILQ_FOREACH(arvif, &sc->vif_list, entry) {
> - if (arvif->vdev_id == ch_info_ev.vdev_id)
> - break;
> - }
> - if (!arvif) {
> - printf("%s: invalid vdev id in chan info ev %d\n",
> - sc->sc_dev.dv_xname, ch_info_ev.vdev_id);
> -#ifdef notyet
> - rcu_read_unlock();
> -#endif
> - return;
> - }
> -#ifdef notyet
> spin_lock_bh(&ar->data_lock);
> #endif
> switch (sc->scan.state) {
> @@ -13705,7 +13693,7 @@ qwx_wmi_process_mgmt_tx_comp(struct qwx_softc *sc,
> struct wmi_mgmt_tx_compl_event *tx_compl_param)
> {
> struct ieee80211com *ic = &sc->sc_ic;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct ifnet *ifp = &ic->ic_if;
> struct qwx_tx_data *tx_data;
>
> @@ -13891,7 +13879,6 @@ void
> qwx_vdev_install_key_compl_event(struct qwx_softc *sc, struct mbuf *m)
> {
> struct wmi_vdev_install_key_complete_arg install_key_compl = { 0 };
> - struct qwx_vif *arvif;
>
> if (qwx_pull_vdev_install_key_compl_ev(sc, m,
> &install_key_compl) != 0) {
> @@ -13906,16 +13893,6 @@ qwx_vdev_install_key_compl_event(struct qwx_softc *sc,
> ether_sprintf((u_char *)install_key_compl.macaddr),
> install_key_compl.status);
>
> - TAILQ_FOREACH(arvif, &sc->vif_list, entry) {
> - if (arvif->vdev_id == install_key_compl.vdev_id)
> - break;
> - }
> - if (!arvif) {
> - printf("%s: invalid vdev id in install key compl ev %d\n",
> - sc->sc_dev.dv_xname, install_key_compl.vdev_id);
> - return;
> - }
> -
> sc->install_key_status = 0;
>
> if (install_key_compl.status !=
> @@ -23475,7 +23452,7 @@ qwx_mac_config_mon_status_default(struct qwx_softc *sc
> int
> qwx_mac_txpower_recalc(struct qwx_softc *sc, struct qwx_pdev *pdev)
> {
> - struct qwx_vif *arvif;
> + struct qwx_vif *arvif = &sc->sc_vif;
> int ret, txpower = -1;
> uint32_t param;
> uint32_t min_tx_power = sc->target_caps.hw_min_tx_power;
> @@ -23483,15 +23460,10 @@ qwx_mac_txpower_recalc(struct qwx_softc *sc, struct qw
> #ifdef notyet
> lockdep_assert_held(&ar->conf_mutex);
> #endif
> - TAILQ_FOREACH(arvif, &sc->vif_list, entry) {
> - if (arvif->txpower <= 0)
> - continue;
> + if (arvif->txpower <= 0)
> + return 0;
>
> - if (txpower == -1)
> - txpower = arvif->txpower;
> - else
> - txpower = MIN(txpower, arvif->txpower);
> - }
> + txpower = MIN(txpower, arvif->txpower);
>
> if (txpower == -1)
> return 0;
> @@ -23664,10 +23636,9 @@ qwx_mac_setup_vdev_params_mbssid(struct qwx_vif *arvif
> }
>
> int
> -qwx_mac_setup_vdev_create_params(struct qwx_vif *arvif, struct qwx_pdev *pdev,
> - struct vdev_create_params *params)
> +qwx_mac_setup_vdev_create_params(struct qwx_softc *sc, struct qwx_vif *arvif,
> + struct qwx_pdev *pdev, struct vdev_create_params *params)
> {
> - struct qwx_softc *sc = arvif->sc;
> int ret;
>
> params->if_id = arvif->vdev_id;
> @@ -23969,70 +23940,45 @@ qwx_mac_vdev_start(struct qwx_softc *sc, struct qwx_vi
> }
>
> void
> -qwx_vif_free(struct qwx_softc *sc, struct qwx_vif *arvif)
> +qwx_vif_free(struct qwx_softc *sc)
> {
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct qwx_txmgmt_queue *txmgmt;
> int i;
>
> - if (arvif == NULL)
> - return;
> -
> txmgmt = &arvif->txmgmt;
> for (i = 0; i < nitems(txmgmt->data); i++) {
> struct qwx_tx_data *tx_data = &txmgmt->data[i];
>
> - if (tx_data->m) {
> - m_freem(tx_data->m);
> - tx_data->m = NULL;
> - }
> if (tx_data->map) {
> bus_dmamap_destroy(sc->sc_dmat, tx_data->map);
> tx_data->map = NULL;
> }
> }
> -
> - free(arvif, M_DEVBUF, sizeof(*arvif));
> }
>
> -void
> -qwx_vif_free_all(struct qwx_softc *sc)
> -{
> - struct qwx_vif *arvif;
> -
> - while (!TAILQ_EMPTY(&sc->vif_list)) {
> - arvif = TAILQ_FIRST(&sc->vif_list);
> - TAILQ_REMOVE(&sc->vif_list, arvif, entry);
> - qwx_vif_free(sc, arvif);
> - }
> -}
> -
> -struct qwx_vif *
> +int
> qwx_vif_alloc(struct qwx_softc *sc)
> {
> - struct qwx_vif *arvif;
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct qwx_txmgmt_queue *txmgmt;
> int i, ret = 0;
> const bus_size_t size = IEEE80211_MAX_LEN;
>
> - arvif = malloc(sizeof(*arvif), M_DEVBUF, M_NOWAIT | M_ZERO);
> - if (arvif == NULL)
> - return NULL;
> -
> + /* Allocate DMA maps used when sending management frames. */
> txmgmt = &arvif->txmgmt;
> for (i = 0; i < nitems(txmgmt->data); i++) {
> struct qwx_tx_data *tx_data = &txmgmt->data[i];
>
> ret = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
> - BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &tx_data->map);
> + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &tx_data->map);
> if (ret) {
> - qwx_vif_free(sc, arvif);
> - return NULL;
> + qwx_vif_free(sc);
> + return ENOMEM;
> }
> }
>
> - arvif->sc = sc;
> -
> - return arvif;
> + return 0;
> }
>
> int
> @@ -24040,7 +23986,7 @@ qwx_mac_op_add_interface(struct qwx_pdev *pdev)
> {
> struct qwx_softc *sc = pdev->sc;
> struct ieee80211com *ic = &sc->sc_ic;
> - struct qwx_vif *arvif = NULL;
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct vdev_create_params vdev_param = { 0 };
> #if 0
> struct peer_create_params peer_param;
> @@ -24071,11 +24017,6 @@ qwx_mac_op_add_interface(struct qwx_pdev *pdev)
> goto err;
> }
>
> - arvif = qwx_vif_alloc(sc);
> - if (arvif == NULL) {
> - ret = ENOMEM;
> - goto err;
> - }
> #if 0
> INIT_DELAYED_WORK(&arvif->connection_loss_work,
> ath11k_mac_vif_sta_connection_loss_work);
> @@ -24130,7 +24071,7 @@ qwx_mac_op_add_interface(struct qwx_pdev *pdev)
> __func__, arvif->vdev_id, arvif->vdev_type,
> arvif->vdev_subtype, sc->free_vdev_map);
>
> - ret = qwx_mac_setup_vdev_create_params(arvif, pdev, &vdev_param);
> + ret = qwx_mac_setup_vdev_create_params(sc, arvif, pdev, &vdev_param);
> if (ret) {
> printf("%s: failed to create vdev parameters %d: %d\n",
> sc->sc_dev.dv_xname, arvif->vdev_id, ret);
> @@ -24150,13 +24091,7 @@ qwx_mac_op_add_interface(struct qwx_pdev *pdev)
> ether_sprintf(ic->ic_myaddr), arvif->vdev_id);
> sc->allocated_vdev_map |= 1U << arvif->vdev_id;
> sc->free_vdev_map &= ~(1U << arvif->vdev_id);
> -#ifdef notyet
> - spin_lock_bh(&ar->data_lock);
> -#endif
> - TAILQ_INSERT_TAIL(&sc->vif_list, arvif, entry);
> -#ifdef notyet
> - spin_unlock_bh(&ar->data_lock);
> -#endif
> +
> ret = qwx_mac_op_update_vif_offload(sc, pdev, arvif);
> if (ret)
> goto err_vdev_del;
> @@ -24294,19 +24229,10 @@ err_peer_del:
> #endif
> err_vdev_del:
> qwx_mac_vdev_delete(sc, arvif);
> -#ifdef notyet
> - spin_lock_bh(&ar->data_lock);
> -#endif
> - TAILQ_REMOVE(&sc->vif_list, arvif, entry);
> -#ifdef notyet
> - spin_unlock_bh(&ar->data_lock);
> -#endif
> -
> err:
> #ifdef notyet
> mutex_unlock(&ar->conf_mutex);
> #endif
> - qwx_vif_free(sc, arvif);
> return ret;
> }
>
> @@ -26096,18 +26022,13 @@ int
> qwx_scan(struct qwx_softc *sc, int bgscan)
> {
> struct ieee80211com *ic = &sc->sc_ic;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list);
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct scan_req_params *arg = NULL;
> struct ieee80211_channel *chan, *lastc;
> int ret = 0, num_channels, i;
> uint32_t scan_timeout;
> int scan_2ghz = 1, scan_5ghz = 1;
>
> - if (arvif == NULL) {
> - printf("%s: no vdev found\n", sc->sc_dev.dv_xname);
> - return EINVAL;
> - }
> -
> /*
> * TODO Will we need separate scan iterations on devices with
> * multiple radios?
> @@ -26356,7 +26277,7 @@ qwx_bgscan_done_task(void *arg)
> {
> struct qwx_softc *sc = arg;
> struct qwx_dp *dp = &sc->dp;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct ieee80211com *ic = &sc->sc_ic;
> struct ifnet *ifp = &ic->ic_if;
> struct ieee80211_node *ni = ic->ic_bss;
> @@ -26589,16 +26510,10 @@ qwx_auth(struct qwx_softc *sc)
> struct ieee80211com *ic = &sc->sc_ic;
> struct ieee80211_node *ni = ic->ic_bss;
> uint32_t param_id;
> - struct qwx_vif *arvif;
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct qwx_pdev *pdev;
> int ret;
>
> - arvif = TAILQ_FIRST(&sc->vif_list);
> - if (arvif == NULL) {
> - printf("%s: no vdev found\n", sc->sc_dev.dv_xname);
> - return EINVAL;
> - }
> -
> pdev = qwx_get_pdev_for_chan(sc, ni->ni_chan);
> if (pdev == NULL) {
> printf("%s: no pdev found for channel %d\n",
> @@ -26641,7 +26556,7 @@ qwx_deauth(struct qwx_softc *sc)
> struct ieee80211com *ic = &sc->sc_ic;
> struct ieee80211_node *ni = ic->ic_bss;
> struct qwx_node *nq = (struct qwx_node *)ni;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct ath11k_peer *peer;
> int ret;
> @@ -26946,7 +26861,7 @@ qwx_updatechan(struct ieee80211com *ic)
> {
> struct ifnet *ifp = &ic->ic_if;
> struct qwx_softc *sc = ifp->if_softc;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list);
> + struct qwx_vif *arvif = &sc->sc_vif;
> struct ieee80211_node *ni = ic->ic_bss;
> struct qwx_node *nq = (struct qwx_node *)ic->ic_bss;
> int pdev_id = 0; /* TODO: derive pdev ID somehow? */
> @@ -27034,7 +26949,7 @@ qwx_rx_agg_start(struct qwx_softc *sc, struct ieee8021
> uint16_t ssn, uint16_t winsize)
> {
> struct ieee80211com *ic = &sc->sc_ic;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> enum hal_pn_type pn_type;
>
> @@ -27055,7 +26970,7 @@ void
> qwx_rx_agg_stop(struct qwx_softc *sc, struct ieee80211_node *ni, uint8_t tid,
> uint16_t ssn, uint16_t winsize, int timeout_val, int start)
> {
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct qwx_node *nq = (struct qwx_node *)ni;
> struct ath11k_peer *peer;
> @@ -27174,7 +27089,7 @@ qwx_assoc(struct qwx_softc *sc)
> struct ieee80211com *ic = &sc->sc_ic;
> struct ieee80211_node *ni = ic->ic_bss;
> struct qwx_node *nq = (struct qwx_node *)ni;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct peer_assoc_params peer_arg;
> int ret;
> @@ -27232,7 +27147,7 @@ qwx_run(struct qwx_softc *sc)
> struct ieee80211com *ic = &sc->sc_ic;
> struct ieee80211_node *ni = ic->ic_bss;
> struct qwx_node *nq = (struct qwx_node *)ni;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct peer_assoc_params peer_arg;
> int ret;
> @@ -27309,7 +27224,7 @@ int
> qwx_run_stop(struct qwx_softc *sc)
> {
> struct ieee80211com *ic = &sc->sc_ic;
> - struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
> + struct qwx_vif *arvif = &sc->sc_vif;
> uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> struct ieee80211_node *ni = ic->ic_bss;
> struct qwx_node *nq = (void *)ni;
> @@ -27377,9 +27292,12 @@ qwx_attach(struct qwx_softc *sc)
> for (i = 0; i < nitems(sc->pdevs); i++)
> sc->pdevs[i].sc = sc;
>
> - TAILQ_INIT(&sc->vif_list);
> TAILQ_INIT(&sc->peers);
>
> + error = qwx_vif_alloc(sc);
> + if (error)
> + return error;
> +
> error = qwx_init(ifp);
> if (error)
> return error;
> @@ -27395,6 +27313,9 @@ qwx_detach(struct qwx_softc *sc)
> {
> qwx_free_peers(sc);
>
> + qwx_vif_purge(sc);
> + qwx_vif_free(sc);
> +
> if (sc->fwmem) {
> qwx_dmamem_free(sc->sc_dmat, sc->fwmem);
> sc->fwmem = NULL;
> blob - e4ae6b82503480d3a351b05f9244868e1a852cf6
> blob + 73001394b0c3bcd13e4075db70d647b0c5a0f645
> --- sys/dev/ic/qwxvar.h
> +++ sys/dev/ic/qwxvar.h
> @@ -1668,11 +1668,8 @@ struct qwx_vif {
> uint8_t hal_addr_search_flags;
> uint8_t search_type;
>
> - struct qwx_softc *sc;
> -
> uint16_t tx_seq_no;
> struct wmi_wmm_params_all_arg wmm_params;
> - TAILQ_ENTRY(qwx_vif) entry;
> union {
> struct {
> uint32_t uapsd;
> @@ -1720,8 +1717,6 @@ struct qwx_vif {
> struct qwx_txmgmt_queue txmgmt;
> };
>
> -TAILQ_HEAD(qwx_vif_list, qwx_vif);
> -
> struct qwx_survey_info {
> int8_t noise;
> uint64_t time;
> @@ -1953,7 +1948,7 @@ struct qwx_softc {
>
> uint32_t pktlog_defs_checksum;
>
> - struct qwx_vif_list vif_list;
> + struct qwx_vif sc_vif;
> struct qwx_pdev pdevs[MAX_RADIOS];
> struct {
> enum WMI_HOST_WLAN_BAND supported_bands;
>
>
qwx use-after-free fix