From: Haotian Jiang <[email protected]>

usb_host_nodev() lazily creates s->bh_nodev and
usb_host_post_load() lazily creates s->bh_postld, but
usb_host_unrealize() never calls qemu_bh_delete() on either.  If a BH
is still pending when unrealize returns, its callback dereferences the
freed USBHostDevice.

Delete both BHs in usb_host_unrealize() after usb_host_close()
returns.  They must be deleted after close because usb_host_close()
can process libusb callbacks that call usb_host_nodev(), which lazily
recreates bh_nodev.  Deleting it before close could therefore leave a
recreated BH alive after unrealize.

Buglink: https://gitlab.com/qemu-project/qemu/-/work_items/3595
Fixes: 95efb20c7148 ("usb-host: live migration support for the libusb version")
Reported-by: Haotian Jiang of Tencent Security (Yunding Lab) 
<[email protected]>
Signed-off-by: Haotian Jiang <[email protected]>
Cc: [email protected]
---
 hw/usb/host-libusb.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index bddd50903ae..8f7ced95cfe 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -1275,6 +1275,21 @@ static void usb_host_unrealize(USBDevice *udev)
     qemu_remove_exit_notifier(&s->exit);
     QTAILQ_REMOVE(&hostdevs, s, next);
     usb_host_close(s);
+    /*
+     * Delete bottom halves after usb_host_close().  During close,
+     * libusb callbacks (usb_host_req_complete_ctrl/data) can call
+     * usb_host_nodev() which lazily creates bh_nodev.  If we
+     * deleted them before close, a newly created BH would leak and
+     * fire after unrealize, dereferencing freed USBHostDevice.
+     */
+    if (s->bh_nodev) {
+        qemu_bh_delete(s->bh_nodev);
+        s->bh_nodev = NULL;
+    }
+    if (s->bh_postld) {
+        qemu_bh_delete(s->bh_postld);
+        s->bh_postld = NULL;
+    }
 }
 
 static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
-- 
2.56.0


Reply via email to