mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
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: 800fe5ec30 ("Bluetooth: btusb: Add support for queuing during polling interval")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
dcaf83ead1
commit
1c12c31176
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user