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)