Commit a86338565c for qemu.org
commit a86338565ceaede2fd7d90a8e0376131a20f0655
Author: Haotian Jiang <sundayjiang@tencent.com>
Date: Fri Oct 16 15:10:38 2026 +0200
usb-host: delete bottom halves during unrealize
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) <sundayjiang@tencent.com>
Signed-off-by: Haotian Jiang <sundayjiang@tencent.com>
Cc: qemu-stable@nongnu.org
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index bddd50903a..8f7ced95cf 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)