usb_host_nodev() lazily creates s->bh_nodev andusb_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 libusb") 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 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1271,6 +1271,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.34.1 发自我的企业微信
