[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]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.