[PATCH] wifi: brcmfmac: Fix potential use-after-free issue when stopping watchdog task

Marek Szyprowski
2026-04-16T09:34:36Z

Watchdog task might end between send_sig() and kthread_stop() calls, what
results in the use-after-free issue. Fix this by increasing watchdog task
reference count before calling send_sig() and dropping it by switching to
kthread_stop_put().

Fixes: 373c83a801f1 ("brcmfmac: stop watchdog before detach and free everything")
Fixes: a9ffda88be74 ("brcm80211: fmac: abstract bus_stop interface function pointer")
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
This fixes the following, rarely observed issue when no firmware binary
is available:

brcmfmac mmc2:0001:1: Direct firmware load for brcm/brcmfmac4330-sdio.txt failed with error -2
------------[ cut here ]------------
------------[ cut here ]------------
WARNING: kernel/fork.c:781 at __put_task_struct+0x13c/0x140, CPU#0: kworker/0:1/10
Modules linked in: brcmfmac hci_uart btbcm btintel bluetooth sha256 cfg80211 s5p_csis s5p_fimc s5p_mfc exynos4_is_common v4l2_fwnode v4l2_async ecdh_generic ecc s5p_jpeg videobuf2_dma_contig v4l2_mem2mem videobuf2_memops videobuf2_v4l2 videobuf2_common videodev brcmutil mc
CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Not tainted 7.0.0-rc6-next-20260402 #12549 PREEMPT
Hardware name: Samsung Exynos (Flattened Device Tree)
Workqueue: events request_firmware_work_func
Call trace:
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x68/0x88
dump_stack_lvl from __warn+0x94/0x204
__warn from warn_slowpath_fmt+0x1b0/0x1bc
warn_slowpath_fmt from __put_task_struct+0x13c/0x140
__put_task_struct from rcu_core+0x330/0x1220
rcu_core from handle_softirqs+0x130/0x5b0
handle_softirqs from __irq_exit_rcu+0x144/0x1f0
__irq_exit_rcu from irq_exit+0x8/0x28
irq_exit from call_with_stack+0x18/0x20
call_with_stack from __irq_svc+0x9c/0xd0
Exception stack(0xe0891c20 to 0xe0891c68)
...
__irq_svc from console_flush_one_record+0x394/0x570
console_flush_one_record from console_unlock+0x78/0x148
console_unlock from vprintk_emit+0x224/0x390
vprintk_emit from vprintk_default+0x20/0x28
vprintk_default from _printk+0x2c/0x5c
_printk from warn_slowpath_fmt+0xe8/0x1bc
warn_slowpath_fmt from kthread_stop+0x2bc/0x364
kthread_stop from brcmf_sdio_remove+0x2c/0x194 [brcmfmac]
brcmf_sdio_remove [brcmfmac] from brcmf_sdiod_remove+0x20/0xb8 [brcmfmac]
brcmf_sdiod_remove [brcmfmac] from brcmf_ops_sdio_remove+0x34/0x5c [brcmfmac]
brcmf_ops_sdio_remove [brcmfmac] from sdio_bus_remove+0x30/0x10c
sdio_bus_remove from device_release_driver_internal+0x190/0x204
device_release_driver_internal from brcmf_sdio_firmware_callback+0x50/0x944 [brcmfmac]
brcmf_sdio_firmware_callback [brcmfmac] from brcmf_fw_request_done+0x154/0x17c [brcmfmac]
brcmf_fw_request_done [brcmfmac] from request_firmware_work_func+0x50/0x98
request_firmware_work_func from process_one_work+0x260/0x7dc
process_one_work from worker_thread+0x1ac/0x3b0
worker_thread from kthread+0x128/0x168
kthread from ret_from_fork+0x14/0x28
Exception stack(0xe0891fb0 to 0xe0891ff8)
...
irq event stamp: 18820
hardirqs last enabled at (18826): [<c01c7d8c>] vprintk_emit+0x364/0x390
hardirqs last disabled at (18831): [<c01c7d48>] vprintk_emit+0x320/0x390
softirqs last enabled at (18598): [<c0add65c>] __alloc_skb+0x168/0x1c0
softirqs last disabled at (18629): [<c013e038>] __irq_exit_rcu+0x144/0x1f0
---[ end trace 0000000000000000 ]---
WARNING: lib/refcount.c:25 at kthread_stop+0x2bc/0x364, CPU#0: kworker/0:1/10
refcount_t: addition on 0; use-after-free.
Modules linked in: brcmfmac hci_uart btbcm btintel bluetooth sha256 cfg80211 s5p_csis s5p_fimc s5p_mfc exynos4_is_common v4l2_fwnode v4l2_async ecdh_generic ecc s5p_jpeg videobuf2_dma_contig v4l2_mem2mem videobuf2_memops videobuf2_v4l2 videobuf2_common videodev brcmutil mc
CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Tainted: G W 7.0.0-rc6-next-20260402 #12549 PREEMPT
Tainted: [W]=WARN
Hardware name: Samsung Exynos (Flattened Device Tree)
Workqueue: events request_firmware_work_func
Call trace:
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x68/0x88
dump_stack_lvl from __warn+0x94/0x204
__warn from warn_slowpath_fmt+0x124/0x1bc
warn_slowpath_fmt from kthread_stop+0x2bc/0x364
kthread_stop from brcmf_sdio_remove+0x2c/0x194 [brcmfmac]
brcmf_sdio_remove [brcmfmac] from brcmf_sdiod_remove+0x20/0xb8 [brcmfmac]
brcmf_sdiod_remove [brcmfmac] from brcmf_ops_sdio_remove+0x34/0x5c [brcmfmac]
brcmf_ops_sdio_remove [brcmfmac] from sdio_bus_remove+0x30/0x10c
sdio_bus_remove from device_release_driver_internal+0x190/0x204
device_release_driver_internal from brcmf_sdio_firmware_callback+0x50/0x944 [brcmfmac]
brcmf_sdio_firmware_callback [brcmfmac] from brcmf_fw_request_done+0x154/0x17c [brcmfmac]
brcmf_fw_request_done [brcmfmac] from request_firmware_work_func+0x50/0x98
request_firmware_work_func from process_one_work+0x260/0x7dc
process_one_work from worker_thread+0x1ac/0x3b0
worker_thread from kthread+0x128/0x168
kthread from ret_from_fork+0x14/0x28
Exception stack(0xe0891fb0 to 0xe0891ff8)
...
irq event stamp: 19327
hardirqs last enabled at (19435): [<c01c3810>] __up_console_sem+0x50/0x60
hardirqs last disabled at (19460): [<c01c37fc>] __up_console_sem+0x3c/0x60
softirqs last enabled at (19458): [<c013dc0c>] handle_softirqs+0x330/0x5b0
softirqs last disabled at (19443): [<c013e038>] __irq_exit_rcu+0x144/0x1f0
---[ end trace 0000000000000000 ]---

Best regards
Marek Szyprowski, PhD
Samsung R&D Institute Poland
---
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index d34db69c25a7..e6de88a6a852 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -2477,8 +2477,9 @@ static void brcmf_sdio_bus_stop(struct device *dev)
brcmf_dbg(TRACE, "Enter\n");

if (bus->watchdog_tsk) {
+ get_task_struct(bus->watchdog_tsk);
send_sig(SIGTERM, bus->watchdog_tsk, 1);
- kthread_stop(bus->watchdog_tsk);
+ kthread_stop_put(bus->watchdog_tsk);
bus->watchdog_tsk = NULL;
}

@@ -4568,8 +4569,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
if (bus) {
/* Stop watchdog task */
if (bus->watchdog_tsk) {
+ get_task_struct(bus->watchdog_tsk);
send_sig(SIGTERM, bus->watchdog_tsk, 1);
- kthread_stop(bus->watchdog_tsk);
+ kthread_stop_put(bus->watchdog_tsk);
bus->watchdog_tsk = NULL;
}

--
2.34.1