[PATCH wireless-next 1/2] wifi: wfx: fix use-after-free of the cooling work on device removal
From: Jérôme Pouiller
Date: Sun Sep 06 2026 - 16:50:26 EST
When the device reports that it is too hot, wfx_suspend_hot_dev() blocks
the Tx queues and arms cooling_timeout_work with a 10s delay. If the
device recovers a normal temperature before the delay expires, the work
is canceled. Else, the work declares the chip frozen and unblocks the Tx
queues.
However, this work is never canceled when the device is removed.
cooling_timeout_work is queued on the system workqueue, while struct
wfx_dev is released by wfx_free_common() (through ieee80211_free_hw()).
So, if the device is unbound during this 10s window, the work fires
after struct wfx_dev has been freed and dereferences it.
Cancel the work during the teardown. It has to be done after
wfx_bh_unregister(): the "device too hot" indication is processed by the
bh, so canceling the work earlier would allow the bh to rearm it. On the
other hand, the work calls wfx_tx_unlock(), which may in turn call
wfx_bh_request_tx(). So it has to be canceled before bh_wq is destroyed.
Note that the Tx queues are intentionally left blocked: the device is
going away, so there is nothing to unblock.
The issue was reported by the Sashiko review bot and the fix has been
written by Copilot (including the commit log). The use-case is difficult
to reproduce, so this code has not been tested. However, I don't believe
this patch could cause any regression.
Fixes: 1d52d29983e5d ("staging: wfx: add support for 'device too hot' indication")
Assisted-by: Sashiko:gemini-3.1-pro-preview
Assisted-by: Copilot:claude-opus-5
Signed-off-by: Jérôme Pouiller <jerome.pouiller@xxxxxxxxxx>
---
drivers/net/wireless/silabs/wfx/main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c
index 4e99fe7e5bb78..b6fd9d4c1f5fc 100644
--- a/drivers/net/wireless/silabs/wfx/main.c
+++ b/drivers/net/wireless/silabs/wfx/main.c
@@ -487,6 +487,7 @@ int wfx_probe(struct wfx_dev *wdev)
wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv);
bh_unregister:
wfx_bh_unregister(wdev);
+ cancel_delayed_work_sync(&wdev->cooling_timeout_work);
destroy_workqueue(wdev->bh_wq);
return err;
}
@@ -497,6 +498,7 @@ void wfx_release(struct wfx_dev *wdev)
wfx_hif_shutdown(wdev);
wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv);
wfx_bh_unregister(wdev);
+ cancel_delayed_work_sync(&wdev->cooling_timeout_work);
destroy_workqueue(wdev->bh_wq);
}
--
2.47.3