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] (cherry picked from commit a86338565ceaede2fd7d90a8e0376131a20f0655) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 735d7c1844a..8dbf66f37b0 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1278,6 +1278,21 @@ static void usb_host_unrealize(USBDevice *udev) 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.47.3
