All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] hw/usb/host-libusb: Avoid assertion on invalid altsetting
@ 2026-08-12  9:24 dongwon.kim
  2026-10-28  5:37 ` Junjie Cao
  0 siblings, 1 reply; 2+ messages in thread
From: dongwon.kim @ 2026-08-12  9:24 UTC (permalink / raw)
  To: qemu-devel

From: Liang1 Yang <liang1.yang@intel.com>

Log warning and skip the interface instead of asserting in qemu
host-libusb when there is invalid altsetting index during fast
USB device hotplug/unplug.

This is to prevent guest vm from crashing which is caused by
QEMU task abort.

Signed-off-by: Liang1 Yang <liang1.yang@intel.com>
Signed-off-by: Dongwon Kim <dongwon.kim@intel.com>
---
 hw/usb/host-libusb.c | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 629505c6d3..56cfc90f9b 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -884,10 +884,32 @@ static void usb_host_ep_update(USBHostDevice *s)
     if (rc != 0) {
         return;
     }
+
+    /* Log and skip if configuration is NULL or has no interfaces */
+    if (!conf || conf->bNumInterfaces == 0) {
+        warn_report("usb-host: ignoring invalid configuration "
+                    "for device %s (bus=%03d, addr=%03d)",
+                    udev->product_desc, s->bus_num, s->addr);
+
+        if (conf) {
+            libusb_free_config_descriptor(conf);
+        }
+
+        return;
+    }
+
     trace_usb_host_parse_config(s->bus_num, s->addr,
                                 conf->bConfigurationValue, true);
 
     for (i = 0; i < conf->bNumInterfaces; i++) {
+        if (conf->interface[i].num_altsetting == 0 ||
+            !conf->interface[i].altsetting) {
+            warn_report("usb-host: skipping interface (index %d) with no "
+                        "alternate settings on %s (bus=%03d, addr=%03d)",
+                        i, udev->product_desc, s->bus_num, s->addr);
+            continue;
+        }
+
         /*
          * The udev->altsetting array indexes alternate settings
          * by the interface number. Get the 0th alternate setting
@@ -897,8 +919,19 @@ static void usb_host_ep_update(USBHostDevice *s)
         intf = &conf->interface[i].altsetting[0];
         alt = udev->altsetting[intf->bInterfaceNumber];
 
-        if (alt != 0) {
-            assert(alt < conf->interface[i].num_altsetting);
+        if (alt > 0) {
+            if (alt >= conf->interface[i].num_altsetting) {
+                /*
+                 * libusb reports a temporary invalid altsetting index during
+                 * fast hotplug/unplug. Instead of aborting, log a warning and
+                 * skip the interface.
+                 */
+                warn_report("usb-host: ignoring out-of-bounds altsetting=%d "
+                            "for interface %d (index %d) on %s (bus=%03d, addr=%03d)",
+                            alt, intf->bInterfaceNumber, i,
+                            udev->product_desc, s->bus_num, s->addr);
+                continue;
+            }
             intf = &conf->interface[i].altsetting[alt];
         }
 
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] hw/usb/host-libusb: Avoid assertion on invalid altsetting
  2026-08-12  9:24 [PATCH] hw/usb/host-libusb: Avoid assertion on invalid altsetting dongwon.kim
@ 2026-10-28  5:37 ` Junjie Cao
  0 siblings, 0 replies; 2+ messages in thread
From: Junjie Cao @ 2026-10-28  5:37 UTC (permalink / raw)
  To: dongwon.kim; +Cc: liang1.yang, qemu-devel, thuth, marcandre.lureau, junjie.cao

On Wed, 12 Aug 2026 02:24:03 -0700, dongwon.kim@intel.com wrote:
> Log warning and skip the interface instead of asserting in qemu
> host-libusb when there is invalid altsetting index during fast
> USB device hotplug/unplug.

Hi Dongwon, Liang,

The out-of-range alt does not come from libusb. It is QEMU's own
udev->altsetting[], written by usb_host_set_interface() when the guest
selects an alternate setting and only cleared in
usb_host_claim_interfaces() on SET_CONFIGURATION. usb_host_open() runs
usb_host_ep_update() before either of those, so when a different device
shows up on the same usb-host instance, the values left behind by the
previous device are checked against the new device's descriptors.

Reproduced on master with -device usb-host,hostbus=3,hostport=4 and a
Debian 12 guest on qemu-xhci: an Arkmicro 18ec:3399 UVC camera, guest
selects interface 1 alt 11 through usbfs, camera unplugged, a Jieli
4c4a:4155 UAC device (interface 1 has two alternate settings) plugged
into the same port. QEMU aborts at host-libusb.c:910 while opening the
second device.

Skipping the interface hides the assert but keeps the stale value, and a
stale value that happens to be in range is not caught at all: it picks
the endpoints of the wrong alternate setting. Clearing udev->altsetting[]
in usb_host_open() before usb_host_ep_update(), the way
usb_host_claim_interfaces() does, removes the cause. The guest has not
selected anything on the new device yet, so zero is the right content,
and the array is written again only after
libusb_set_interface_alt_setting() succeeds, so the assert can stay.
udev->ninterfaces and udev->configuration go stale the same way; nothing
in usb-host reads them, but moving the three resets from
usb_host_claim_interfaces() into a helper called from both places keeps
them together. With udev->altsetting[] cleared the sequence above
enumerates the second device, snd-usb-audio and usbhid bind to it and
its own SET_INTERFACE round trips go through.

> +                warn_report("usb-host: ignoring out-of-bounds altsetting=%d "
> +                            "for interface %d (index %d) on %s (bus=%03d, addr=%03d)",
> +                            alt, intf->bInterfaceNumber, i,
> +                            udev->product_desc, s->bus_num, s->addr);

usb_host_open() fills udev->product_desc after usb_host_ep_update(), so
this names the previous device. With the patch rebased onto master the
sequence above prints

  usb-host: ignoring out-of-bounds altsetting=11 for interface 1 (index 1)
  on USB2.0 PC CAMERA (bus=003, addr=014)

for the audio device.

> +    /* Log and skip if configuration is NULL or has no interfaces */
> +    if (!conf || conf->bNumInterfaces == 0) {

libusb_get_active_config_descriptor() sets *config whenever it returns
0, so !conf cannot happen, and bNumInterfaces == 0 already falls through
the loop.

The patch also no longer applies on master: e1b7f5e2a6 ("usb-host:
validate interface number before indexing altsetting") changed the same
loop, and conf is a g_autoptr there since 322894eed3, so the explicit
libusb_free_config_descriptor() in the new early return would be a
double free. A v2 should carry
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3189 and
Cc: qemu-stable@nongnu.org, the assert is reachable from 9.1.0 as
reported there.

Thanks,
Junjie


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-28  5:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12  9:24 [PATCH] hw/usb/host-libusb: Avoid assertion on invalid altsetting dongwon.kim
2026-10-28  5:37 ` Junjie Cao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.