diff options
| author | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2026-08-31 12:13:10 -0400 |
|---|---|---|
| committer | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2026-09-08 17:03:32 -0400 |
| commit | 1c12c3117639e78940959d956519c758c57d0849 (patch) | |
| tree | 2f7514f91c5d63db42afe1b840e6decbba5c8b0f /drivers/bluetooth | |
| parent | dcaf83ead130d3067862599089b0999b3da140a4 (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.c | 14 |
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); |
