diff --git a/sys/dev/bwn/if_bwn.c b/sys/dev/bwn/if_bwn.c index 38bf6f5d..7a5b4230 100644 --- a/sys/dev/bwn/if_bwn.c +++ b/sys/dev/bwn/if_bwn.c @@ -147,6 +147,7 @@ static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void bwn_updateslot(struct ieee80211com *); static void bwn_update_promisc(struct ieee80211com *); +static void bwn_update_mcast(struct ieee80211com *); static void bwn_wme_init(struct bwn_mac *); static int bwn_wme_update(struct ieee80211com *); static void bwn_wme_clear(struct bwn_softc *); @@ -250,6 +251,7 @@ static void bwn_dma_setup(struct bwn_dma_ring *); static void bwn_dma_free_ringmemory(struct bwn_dma_ring *); static void bwn_dma_cleanup(struct bwn_dma_ring *); static void bwn_dma_free_descbufs(struct bwn_dma_ring *); +static void bwn_dma_reclaim_tx(struct bwn_mac *); static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int); static void bwn_dma_rx(struct bwn_dma_ring *); static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int); @@ -812,6 +814,7 @@ bwn_attach_post(struct bwn_softc *sc) ic->ic_raw_xmit = bwn_raw_xmit; ic->ic_updateslot = bwn_updateslot; ic->ic_update_promisc = bwn_update_promisc; + ic->ic_update_mcast = bwn_update_mcast; ic->ic_wme.wme_update = bwn_wme_update; ic->ic_scan_start = bwn_scan_start; ic->ic_scan_end = bwn_scan_end; @@ -855,7 +858,6 @@ bwn_detach(device_t dev) BWN_LOCK(sc); bwn_stop(sc); BWN_UNLOCK(sc); - bwn_dma_free(mac); callout_drain(&sc->sc_led_blink_ch); callout_drain(&sc->sc_rfswitch_ch); callout_drain(&sc->sc_task_ch); @@ -863,7 +865,17 @@ bwn_detach(device_t dev) bwn_phy_detach(mac); ieee80211_draintask(ic, &mac->mac_hwreset); ieee80211_draintask(ic, &mac->mac_txpower); + /* + * Drop any TX-ring node references BEFORE ieee80211_ifdetach() + * frees all nodes. bwn_dma_free()/detach would otherwise find a + * dangling node pointer (the ring slot still holds mt_ni for a + * frame whose TX completion never ran) and double-free it + * (vmcore.5). This is independent of mac_status: a frame can be + * queued in a ring without the MAC ever reaching STARTED. + */ + bwn_dma_reclaim_tx(mac); ieee80211_ifdetach(ic); + bwn_dma_free(mac); } taskqueue_drain(sc->sc_tq, &mac->mac_intrtask); taskqueue_free(sc->sc_tq); @@ -1094,7 +1106,12 @@ bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, tq = bwn_pio_select(mac, M_WME_GETAC(m)); KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__)); tp = TAILQ_FIRST(&tq->tq_pktlist); - tp->tp_ni = ni; + /* + * Take our own reference on the node (see bwn_dma_tx_start for the + * rationale); the PIO path otherwise shares the same use-after-free + * hazard as the DMA path. + */ + tp->tp_ni = ieee80211_ref_node(ni); tp->tp_m = m; error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp)); @@ -1218,7 +1235,15 @@ bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY && mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__)); mt->mt_m = m; - mt->mt_ni = ni; + /* + * Take our own reference on the node. The mbuf's rcvif reference + * belongs to the net80211 stack and may be released (e.g. when the + * VAP is torn down or a scan is cancelled) while the frame is still + * pending in the TX ring. Without our own reference the slot would + * point at a freed node, and freeing it later (on TX completion or + * at detach) would double-free / use-after-free and panic. + */ + mt->mt_ni = ieee80211_ref_node(ni); error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m, bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT); @@ -1892,6 +1917,33 @@ bwn_update_promisc(struct ieee80211com *ic) BWN_UNLOCK(sc); } +static void +bwn_update_mcast(struct ieee80211com *ic) +{ + struct bwn_softc *sc = ic->ic_softc; + struct bwn_mac *mac; + + /* + * bwn has no hardware multicast hash filter; the only RX filter bit + * available for multicast is BWN_MACCTL_PROMISC (the same bit used by + * bwn_update_promisc). When any VAP needs all-multicast (e.g. for + * IPv6 ND/MLD), set PROMISC so the frames are passed up to net80211, + * which then does the multicast filtering in software. Without this + * callback net80211 uses the null stub and prints + * "need multicast update callback", and IPv6 multicast breaks. + */ + BWN_LOCK(sc); + mac = sc->sc_curmac; + if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) { + if (ic->ic_allmulti > 0) + sc->sc_filters |= BWN_MACCTL_PROMISC; + else + sc->sc_filters &= ~BWN_MACCTL_PROMISC; + bwn_set_opmode(mac); + } + BWN_UNLOCK(sc); +} + /* * Callback from the 802.11 layer to update WME parameters. */ @@ -2999,7 +3051,17 @@ static void bwn_dma_ringfree(struct bwn_dma_ring **dr) { - if (dr == NULL) + /* + * bwn_dma_attach() sets BWN_MAC_FLAG_DMA early, before the individual + * rings (rx/wme[]/mcast) are allocated via bwn_dma_ringsetup(). If + * attach fails partway through (or the device is detached after a + * partial attach), bwn_dma_free() is still invoked because the flag is + * set, but the ring pointers are still NULL. Dereferencing them here + * faults (the crash in bwn_dma_ringfree+0x14c reading at offset 0x30, + * i.e. (*dr)->dr_mac). Guard against a NULL ring so teardown after a + * partial attach is safe. + */ + if (dr == NULL || *dr == NULL) return; bwn_dma_free_descbufs(*dr); @@ -3364,8 +3426,18 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr) struct bwn_softc *sc = mac->mac_sc; int i; - if (!dr->dr_usedslot) - return; + /* + * Do NOT skip this loop based on dr_usedslot. dr_usedslot is only an + * accounting hint for free-slot tracking and is not a reliable + * indicator of whether a frame (and its node reference) is pending; + * after a HW reset / ring re-init it can be inconsistent with the + * actual slot contents. We must unconditionally walk every slot and + * release any mbuf + node reference still attached, otherwise a + * dangling node pointer survives to bwn_dma_free()/detach and + * double-frees the node (vmcore.5). bwn_dma_free_descbuf() is + * idempotent (NULLs mt_m/mt_ni and skips empty slots), so walking + * all slots is safe even when none hold a frame. + */ for (i = 0; i < dr->dr_numslots; i++) { dr->getdesc(dr, i, &desc, &meta); @@ -3388,6 +3460,31 @@ bwn_dma_free_descbufs(struct bwn_dma_ring *dr) } } +static void +bwn_dma_reclaim_tx(struct bwn_mac *mac) +{ + struct bwn_dma *dma; + + if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0) + return; + dma = &mac->mac_method.dma; + + /* + * Drop the mbuf + ieee80211_node references for any frame still + * pending in the TX rings. This must be called BEFORE + * ieee80211_ifdetach() (which frees all nodes) and is safe to + * call even if the MAC was never fully started, because a + * frame can be queued in a ring without the MAC reaching + * STARTED (e.g. the watchdog fired right after a transmit). + * See Fix 6/7 and vmcore.5 (node double-free) / vmcore.4. + */ + bwn_dma_free_descbufs(dma->wme[WME_AC_BK]); + bwn_dma_free_descbufs(dma->wme[WME_AC_BE]); + bwn_dma_free_descbufs(dma->wme[WME_AC_VI]); + bwn_dma_free_descbufs(dma->wme[WME_AC_VO]); + bwn_dma_free_descbufs(dma->mcast); +} + static int bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base, int type) @@ -7349,6 +7446,45 @@ bwn_dma_stop(struct bwn_mac *mac) bwn_dma_ringstop(&dma->wme[WME_AC_VI]); bwn_dma_ringstop(&dma->wme[WME_AC_VO]); bwn_dma_ringstop(&dma->mcast); + + /* + * Reclaim any frames still pending in the DMA rings. + * + * When TX never completes (e.g. interrupts are not delivered, which + * on this chip manifests as "device timeout"), the ring slots keep + * their mbuf + ieee80211_node references until a TX-completion + * callback (bwn_dma_handle_txeof) runs. bwn_dma_handle_txeof is the + * ONLY place that clears mt_ni, so without it the references linger + * here. If net80211 later frees that node (scan cancel / VAP + * teardown) while we still point at it, the reference becomes + * dangling; then bwn_dma_free() -> bwn_dma_ringfree() -> + * bwn_dma_free_descbufs() -> ieee80211_free_node() walks freed + * memory and panics (vmcore.4: page fault in node_cleanup / + * ieee80211_ageq_remove). + * + * Releasing the pending frames here, at device-stop time, drops the + * node references while the node is still alive (this runs before + * ieee80211_ifdetach() during detach and during every HW reset), + * so no dangling reference survives to detach. bwn_dma_free_descbufs + * is idempotent (it NULLs mt_m/mt_ni), so the later free at detach + * simply finds empty slots and skips them. + * + * IMPORTANT: only the TX rings (wme[] + mcast) are reclaimed here. + * The RX ring MUST NOT be reclaimed at stop time: bwn_dma_stop() runs + * on every HW reset (every "device timeout"), and bwn_dma_free_descbufs() + * frees the RX mbuf WITHOUT clearing the DMA descriptor's physical + * address. The RX ring is persistent (it is refilled in place by + * bwn_rxeof() -> bwn_dma_newbuf(), never re-allocated), so after a + * reset the descriptor would still point at freed memory and the NIC + * would DMA received frames into freed kernel memory -> heap + * corruption (vmcore.6: TCP sb_mb NULL while sb_ccc != 0). Reclaiming + * the RX ring here also bought nothing: the RX ring holds no node + * reference, so it cannot cause the node double-free this fix targets. + * + * The actual TX-ring reclaim is factored into bwn_dma_reclaim_tx() + * so it can also be called from bwn_detach() before ieee80211_ifdetach(). + */ + bwn_dma_reclaim_tx(mac); } static void