This patch fixes a hard system freeze (requires power cycle) observed when unplugging an AR9170 USB WiFi adapter while under traffic, or when any driver that uses ieee80211_reconfig() encounters a firmware deadlock. The race: ieee80211_handle_reconfig_failure() tears down station tables while USB RX tasklets can still deliver frames that reference them (use-after-free). Calling drv_stop() before clearing IN_DRIVER state closes the race at the root cause. Tested-by: Masi Osmani [AR9170 USB, Linux 6.18] --- When ieee80211_handle_reconfig_failure() is called after a failed HW reconfiguration, it clears IEEE80211_SDATA_IN_DRIVER flags on all interfaces but does not stop the hardware. This creates a race window: cfg80211_shutdown_all_interfaces() subsequently calls ieee80211_do_stop() which runs sta_info_flush() to destroy stations, while the driver's RX path may still be delivering frames that reference station data being freed. This race was observed with the carl9170 driver: when firmware deadlocks during a restart attempt, ieee80211_reconfig() fails at drv_add_interface(). The subsequent interface teardown triggers sta_info_destroy_part2() while the USB RX tasklet still calls ieee80211_rx_napi(), causing a use-after-free kernel panic. The fix stops the hardware in ieee80211_handle_reconfig_failure() before clearing IN_DRIVER state, ensuring no driver can deliver RX frames once the teardown begins. The drv_stop() call is guarded by local->started since some call sites reach this function after drv_start() has already failed (where the hardware was never started). Signed-off-by: Masi Osmani --- net/mac80211/util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/mac80211/util.c b/net/mac80211/util.c --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1614,6 +1614,18 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) local->resuming = false; local->suspended = false; + + /* + * Stop the hardware before clearing IN_DRIVER state. Without this, + * cfg80211_shutdown_all_interfaces() tears down stations via + * sta_info_flush() while the driver's RX path may still deliver + * frames referencing station data being freed, causing use-after-free. + * Guard with local->started since this function can be reached when + * drv_start() itself failed (hardware never started). + */ + if (local->started) + drv_stop(local, false); + local->in_reconfig = false; local->reconfig_failure = true; -- Regards, Masi Osmani