summaryrefslogtreecommitdiff
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2026-08-31 12:13:10 -0400
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2026-09-08 17:03:32 -0400
commit1c12c3117639e78940959d956519c758c57d0849 (patch)
tree2f7514f91c5d63db42afe1b840e6decbba5c8b0f /drivers/bluetooth
parentdcaf83ead130d3067862599089b0999b3da140a4 (diff)
Bluetooth: btusb: Fix UAF of btusb_data by rx_work
btusb_close() and btusb_flush() cancel data->rx_work with the asynchronous cancel_delayed_work(), so if btusb_rx_work() is already running on another CPU it keeps running after the cancel returns. btusb_disconnect() calls hci_unregister_dev(), which invokes btusb_close(), and then frees the btusb_data. A still running btusb_rx_work() then dereferences the freed data: while ((skb = skb_dequeue(&data->acl_q))) data->recv_acl(data->hdev, skb); Use cancel_delayed_work_sync() instead. In btusb_close() the cancel also has to happen after btusb_stop_traffic(), otherwise an URB completion racing with the cancel can requeue the work right after it has been waited for. Fixes: 800fe5ec302e ("Bluetooth: btusb: Add support for queuing during polling interval") Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/btusb.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 95f4640c60e4..ddc44ca28722 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2094,18 +2094,24 @@ static int btusb_close(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
- cancel_delayed_work(&data->rx_work);
cancel_work_sync(&data->work);
cancel_work_sync(&data->waker);
- skb_queue_purge(&data->acl_q);
-
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
btusb_stop_traffic(data);
+
+ /* rx_work must only be canceled once the URBs that can rearm it are
+ * gone, and it must be canceled synchronously since btusb_disconnect()
+ * frees the btusb_data it dereferences right after hci_unregister_dev().
+ */
+ cancel_delayed_work_sync(&data->rx_work);
+
+ skb_queue_purge(&data->acl_q);
+
btusb_free_frags(data);
err = usb_autopm_get_interface(data->intf);
@@ -2131,7 +2137,7 @@ static int btusb_flush(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
- cancel_delayed_work(&data->rx_work);
+ cancel_delayed_work_sync(&data->rx_work);
skb_queue_purge(&data->acl_q);