[PATCH] Bluetooth: virtio_bt: Fix use-after-free and memory leak in probe error paths
From: ZhaoJinming
Date: Tue Aug 11 2026 - 04:52:01 EST
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 <zhaojinming@xxxxxxxxxxxxx>
---
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 <zhaojinming@xxxxxxxxxxxxx>