[PATCH] wifi: libertas: cancel fw_timeout on disconnect

YuanZhe Liu 25031212351 at stu.xidian.edu.cn
Sat Oct 17 00:45:49 PDT 2026


if_usb_disconnect() frees the if_usb_card structure without ever cancelling
its fw_timeout timer, which can still be pending in the timer wheel at that
point.

That timer is armed by the RX completion handler if_usb_receive_fwload()
with a five second deadline while a firmware download is in progress.  The
only place that cancels it is the asynchronous firmware download callback
if_usb_prog_firmware(), and it does so before killing the RX URBs:

	wait_event_interruptible(cardp->fw_wq,
				 cardp->surprise_removed ||
				 cardp->fwdnldover);
	timer_delete_sync(&cardp->fw_timeout);
	usb_kill_anchored_urbs(&cardp->rx_submitted);

Killing the URBs does not undo a timer that a completion handler which was
already running re-armed in between, so fw_timeout can still be pending
when the callback returns.

If the device is disconnected at that point, for example after a
USBDEVFS_RESET forced unbind or a physical unplug during firmware download,
the card structure is freed with its timer still queued in a timer wheel
bucket:

	BUG: KASAN: slab-use-after-free in enqueue_timer+0x236/0x250
	Write of size 8 at addr ffff88800ed324c8

	Allocated by task 1623:
	 if_usb_probe+0xc8/0xc60
	 drivers/net/wireless/marvell/libertas/if_usb.c:205
	Freed by task 1686:
	 if_usb_disconnect+0x1c6/0x270
	 drivers/net/wireless/marvell/libertas/if_usb.c:322

	The buggy address is located 200 bytes inside of
	 freed 512-byte region [ffff88800ed32400, ffff88800ed32600)

The offset corresponds to fw_timeout.entry.pprev, so the timer core later
rewrites the list node of a timer living in freed memory, both when
enqueuing another timer into the same bucket and when moving the bucket to
the expired list in run_timer_softirq().

Cancel the timer in if_usb_disconnect(), after if_usb_free() has killed the
RX URBs, which stops the completion path from arming it again, and before
the card is freed.

Fixes: 4f82f5c853e3 ("libertas: switch to a waitqueue and timer for handling USB firmware load")
Co-developed-by: Bin Yu <byu at xidian.edu.cn>
Signed-off-by: Bin Yu <byu at xidian.edu.cn>
Co-developed-by: Cheng Wen <wencheng at xidian.edu.cn>
Signed-off-by: Cheng Wen <wencheng at xidian.edu.cn>
Signed-off-by: YuanZhe Liu <25031212351 at stu.xidian.edu.cn>
---
 drivers/net/wireless/marvell/libertas/if_usb.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index 5cc0c5cac257..3410d6850db2 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
@@ -319,6 +319,12 @@ static void if_usb_disconnect(struct usb_interface *intf)
 
 	/* Unlink and free urb */
 	if_usb_free(cardp);
+
+	/* A firmware download in progress can re-arm fw_timeout from the RX
+	 * completion path, so make sure it is gone before freeing the card.
+	 */
+	timer_delete_sync(&cardp->fw_timeout);
+
 	kfree(cardp);
 
 	usb_set_intfdata(intf, NULL);
-- 
2.34.1




More information about the libertas-dev mailing list