[PATCH] Bluetooth: virtio_bt: Fix use-after-free and memory leak in probe error paths
ZhaoJinming <[email protected]>
| Newsgroups | org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260811-virtio-bt-fix-probe-errors-v1-1-2f1acfde8336@uniontech.com> |
When virtbt_open_vdev() fails in virtbt_probe(), hci_free_dev(hdev) is called without first calling hci_unregister_dev(hdev). Since hci_register_dev() already succeeded, the HCI device remains registered while its memory is freed, leading to a use-after-free when accessed via sysfs or HCI sockets. Additionally, the probe function leaks the virtio_bluetooth structure (vbt) in several error paths: - When virtio_find_vqs() fails, vbt is not freed. - When hci_alloc_dev() or hci_register_dev() fails, vbt is not freed. - When virtbt_open_vdev() fails, vbt is not freed. Furthermore, when virtbt_open_vdev() fails after virtio_device_ready() has been called, the device is left live (DRIVER_OK set) while its virtqueues are torn down, and any scheduled work is not flushed, potentially allowing a use-after-free from device-initiated callbacks. Fix all of these by restructuring the error labels to properly unwind in reverse order of the allocation/registration sequence. The new labels err_del_vqs and err_free_vbt ensure that del_vqs and kfree(vbt) are called as appropriate for each failure point. For the virtbt_open_vdev() failure path, call virtio_reset_device() and virtbt_close_vdev() before unregistering the HCI device, matching the cleanup pattern in virtbt_remove(). Signed-off-by: ZhaoJinming <[email protected]> --- Changes in v1: - Initial submission. --- drivers/bluetooth/virtio_bt.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c index 140ab55c9fc5a973d6b0034ca11d026dfed71ef2..4be6e106d6985fea5a5786ab68497b17d14f66b8 100644 --- a/drivers/bluetooth/virtio_bt.c +++ b/drivers/bluetooth/virtio_bt.c @@ -311,12 +311,12 @@ static int virtbt_probe(struct virtio_device *vdev) err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, vqs_info, NULL); if (err) - return err; + goto err_free_vbt; hdev = hci_alloc_dev(); if (!hdev) { err = -ENOMEM; - goto failed; + goto err_del_vqs; } vbt->hdev = hdev; @@ -386,20 +386,25 @@ static int virtbt_probe(struct virtio_device *vdev) if (hci_register_dev(hdev) < 0) { hci_free_dev(hdev); err = -EBUSY; - goto failed; + goto err_del_vqs; } virtio_device_ready(vdev); err = virtbt_open_vdev(vbt); - if (err) - goto open_failed; + if (err) { + hci_unregister_dev(hdev); + virtio_reset_device(vdev); + virtbt_close_vdev(vbt); + hci_free_dev(hdev); + goto err_del_vqs; + } return 0; -open_failed: - hci_free_dev(hdev); -failed: +err_del_vqs: vdev->config->del_vqs(vdev); +err_free_vbt: + kfree(vbt); return err; } --- base-commit: d58772d8520c7ef247c4b95c9bd76d3a25da9ff5 change-id: 20260811-virtio-bt-fix-probe-errors-d3ee0fa545f9 Best regards, -- ZhaoJinming <[email protected]>