From: Haotian Jiang <[email protected]> When libusb_submit_transfer() fails in usb_host_handle_control() or usb_host_handle_data(), the USBHostRequest already queued via usb_host_req_alloc() is never freed and its r->p pointer is never cleared. The caller (xHCI) may then complete and free the XHCITransfer that embeds the USBPacket r->p points to, leaving a dangling pointer in s->requests. A subsequent usb_host_abort_xfers() -> usb_host_req_abort() dereferences r->p->state on already-freed memory.
Call usb_host_req_free(r) before returning, matching the existing pattern in the #else branch of the BULK_STREAMS case. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3595 Fixes: 2b2325ff6491 ("use libusb for usb-host") 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 59cb5109101330fdc4588cbd5a081deeed0cd493) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 88fea325531..45f7bab53fc 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1529,6 +1529,13 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, CONTROL_TIMEOUT); rc = libusb_submit_transfer(r->xfer); if (rc != 0) { + /* + * Free the request to avoid a dangling r->p pointer. + * When libusb_submit_transfer fails the caller (xHCI) may + * free the XHCITransfer that holds the USBPacket r->p points + * to, so we must remove r from s->requests before returning. + */ + usb_host_req_free(r); p->status = USB_RET_NODEV; trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status, p->actual_length); @@ -1623,6 +1630,13 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p) rc = libusb_submit_transfer(r->xfer); if (rc != 0) { + /* + * Free the request to avoid a dangling r->p pointer. + * When libusb_submit_transfer fails the caller (xHCI) may + * free the XHCITransfer that holds the USBPacket r->p points + * to, so we must remove r from s->requests before returning. + */ + usb_host_req_free(r); p->status = USB_RET_NODEV; trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status, p->actual_length); -- 2.47.3
