* [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable()
@ 2026-03-30 23:06 Michal Pecio
2026-04-01 14:34 ` Mathias Nyman
0 siblings, 1 reply; 4+ messages in thread
From: Michal Pecio @ 2026-03-30 23:06 UTC (permalink / raw)
To: Mathias Nyman, Greg Kroah-Hartman; +Cc: Alan Stern, linux-usb, linux-kernel
xHCI hardware maintains its endpoint state between add_endpoint()
and drop_endpoint() calls followed by successful check_bandwidth().
So does the driver.
Core may call endpoint_disable() during xHCI endpoint life, so don't
clear host_ep->hcpriv then, because this breaks endpoint_reset().
If a driver calls usb_set_interface(), submits URBs which make host
sequence state non-zero and calls usb_clear_halt(), the device clears
its sequence state but xhci_endpoint_reset() bails out. The next URB
malfunctions: USB2 loses one packet, USB3 gets Transaction Error or
may not complete at all on some (buggy?) HCs from ASMedia and AMD.
This is triggered by uvcvideo on bulk video devices.
The code was copied from ehci_endpoint_disable() but it isn't needed
here - hcpriv should only be NULL on emulated root hub endpoints.
It might prevent resetting and inadvertently enabling a disabled and
dropped endpoint, but core shouldn't try to reset dropped endpoints.
Document xhci requirements regarding hcpriv. They are currently met.
Fixes: 18b74067ac78 ("xhci: Fix use-after-free regression in xhci clear hub TT implementation")
Cc: stable@vger.kernel.org
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
---
drivers/usb/host/xhci.c | 1 -
include/linux/usb.h | 3 ++-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 2f7e6544e5ae..849a568d0e63 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3353,7 +3353,6 @@ static void xhci_endpoint_disable(struct usb_hcd *hcd,
xhci_dbg(xhci, "endpoint disable with ep_state 0x%x\n",
ep->ep_state);
done:
- host_ep->hcpriv = NULL;
spin_unlock_irqrestore(&xhci->lock, flags);
}
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 04277af4bb9d..27e95eade121 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -54,7 +54,8 @@ struct ep_device;
* @eusb2_isoc_ep_comp: eUSB2 isoc companion descriptor for this endpoint
* @urb_list: urbs queued to this endpoint; maintained by usbcore
* @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
- * with one or more transfer descriptors (TDs) per urb
+ * with one or more transfer descriptors (TDs) per urb; must be preserved
+ * by core while BW is allocated for the endpoint
* @ep_dev: ep_device for sysfs info
* @extra: descriptors following this endpoint in the configuration
* @extralen: how many bytes of "extra" are valid
--
2.48.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable()
2026-03-30 23:06 [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable() Michal Pecio
@ 2026-04-01 14:34 ` Mathias Nyman
2026-04-01 14:52 ` Michal Pecio
0 siblings, 1 reply; 4+ messages in thread
From: Mathias Nyman @ 2026-04-01 14:34 UTC (permalink / raw)
To: Michal Pecio, Mathias Nyman, Greg Kroah-Hartman
Cc: Alan Stern, linux-usb, linux-kernel
On 3/31/26 02:06, Michal Pecio wrote:
> xHCI hardware maintains its endpoint state between add_endpoint()
> and drop_endpoint() calls followed by successful check_bandwidth().
> So does the driver.
>
> Core may call endpoint_disable() during xHCI endpoint life, so don't
> clear host_ep->hcpriv then, because this breaks endpoint_reset().
>
> If a driver calls usb_set_interface(), submits URBs which make host
> sequence state non-zero and calls usb_clear_halt(), the device clears
> its sequence state but xhci_endpoint_reset() bails out. The next URB
> malfunctions: USB2 loses one packet, USB3 gets Transaction Error or
> may not complete at all on some (buggy?) HCs from ASMedia and AMD.
> This is triggered by uvcvideo on bulk video devices.
Were you able to trigger a usb_clear_halt() called with ep->hcpriv == NULL,
causing a toggle/seq mismatch?
The ep->hcpriv should be set back correctly in usb_set_interface():
usb_set_interface()
usb_hcd_alloc_bandwidth()
hcd->driver->add_endpoint()
xhci_add_endpoint()
ep->hcpriv = udev;
I'm not against this patch, but would like to understand how we end here
Thanks
Mathias
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable()
2026-04-01 14:34 ` Mathias Nyman
@ 2026-04-01 14:52 ` Michal Pecio
2026-04-01 19:27 ` Mathias Nyman
0 siblings, 1 reply; 4+ messages in thread
From: Michal Pecio @ 2026-04-01 14:52 UTC (permalink / raw)
To: Mathias Nyman
Cc: Mathias Nyman, Greg Kroah-Hartman, Alan Stern, linux-usb,
linux-kernel
On Wed, 1 Apr 2026 17:34:37 +0300, Mathias Nyman wrote:
> On 3/31/26 02:06, Michal Pecio wrote:
> > xHCI hardware maintains its endpoint state between add_endpoint()
> > and drop_endpoint() calls followed by successful check_bandwidth().
> > So does the driver.
> >
> > Core may call endpoint_disable() during xHCI endpoint life, so don't
> > clear host_ep->hcpriv then, because this breaks endpoint_reset().
> >
> > If a driver calls usb_set_interface(), submits URBs which make host
> > sequence state non-zero and calls usb_clear_halt(), the device clears
> > its sequence state but xhci_endpoint_reset() bails out. The next URB
> > malfunctions: USB2 loses one packet, USB3 gets Transaction Error or
> > may not complete at all on some (buggy?) HCs from ASMedia and AMD.
> > This is triggered by uvcvideo on bulk video devices.
>
> Were you able to trigger a usb_clear_halt() called with ep->hcpriv == NULL,
> causing a toggle/seq mismatch?
>
> The ep->hcpriv should be set back correctly in usb_set_interface():
>
> usb_set_interface()
> usb_hcd_alloc_bandwidth()
> hcd->driver->add_endpoint()
> xhci_add_endpoint()
> ep->hcpriv = udev;
right, and later:
usb_disable_interface(dev, iface, true)
usb_disable_endpoint(dev, ..., true)
usb_hcd_disable_endpoint(dev, ep)
hcd->driver->endpoint_disable(hcd, ep)
usb_enable_interface(dev, iface, true)
usb_enable_endpoint(dev, ..., true)
usb_hcd_reset_endpoint(dev, ep)
hcd->driver->endpoint_reset(hcd, ep)
So it seems set_interface() is broken and doesn't actually reset host
sequence state (while the device is supposed to reset its own).
This alone is rarely a problem because the endpoint is usually "fresh".
But uvcvideo calls usb_clear_halt() *after* stopping a bulk stream,
because that's what Windows does. Then sequence state is random and
gets cleared only on the device, because hcpriv is still NULL.
The next URB gets Transaction Error, the host endpoint is reset and
another URB succeeds (because we restart the endpoint unconditionally).
Regards,
Michal
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable()
2026-04-01 14:52 ` Michal Pecio
@ 2026-04-01 19:27 ` Mathias Nyman
0 siblings, 0 replies; 4+ messages in thread
From: Mathias Nyman @ 2026-04-01 19:27 UTC (permalink / raw)
To: Michal Pecio
Cc: Mathias Nyman, Greg Kroah-Hartman, Alan Stern, linux-usb,
linux-kernel
On 4/1/26 17:52, Michal Pecio wrote:
> On Wed, 1 Apr 2026 17:34:37 +0300, Mathias Nyman wrote:
>> On 3/31/26 02:06, Michal Pecio wrote:
>>> xHCI hardware maintains its endpoint state between add_endpoint()
>>> and drop_endpoint() calls followed by successful check_bandwidth().
>>> So does the driver.
>>>
>>> Core may call endpoint_disable() during xHCI endpoint life, so don't
>>> clear host_ep->hcpriv then, because this breaks endpoint_reset().
>>>
>>> If a driver calls usb_set_interface(), submits URBs which make host
>>> sequence state non-zero and calls usb_clear_halt(), the device clears
>>> its sequence state but xhci_endpoint_reset() bails out. The next URB
>>> malfunctions: USB2 loses one packet, USB3 gets Transaction Error or
>>> may not complete at all on some (buggy?) HCs from ASMedia and AMD.
>>> This is triggered by uvcvideo on bulk video devices.
>>
>> Were you able to trigger a usb_clear_halt() called with ep->hcpriv == NULL,
>> causing a toggle/seq mismatch?
>>
>> The ep->hcpriv should be set back correctly in usb_set_interface():
>>
>> usb_set_interface()
>> usb_hcd_alloc_bandwidth()
>> hcd->driver->add_endpoint()
>> xhci_add_endpoint()
>> ep->hcpriv = udev;
>
> right, and later:
>
> usb_disable_interface(dev, iface, true)
> usb_disable_endpoint(dev, ..., true)
> usb_hcd_disable_endpoint(dev, ep)
> hcd->driver->endpoint_disable(hcd, ep)
> usb_enable_interface(dev, iface, true)
> usb_enable_endpoint(dev, ..., true)
> usb_hcd_reset_endpoint(dev, ep)
> hcd->driver->endpoint_reset(hcd, ep)
>
True, thanks, usb_set_interface() calls usb_disable_interface() twice.
First time just to flush pending URBs, second time with reset_hardware
flag set.
Adding patch to queue
-Mathias
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-01 19:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-30 23:06 [PATCH] usb: xhci: Make usb_host_endpoint.hcpriv survive endpoint_disable() Michal Pecio
2026-04-01 14:34 ` Mathias Nyman
2026-04-01 14:52 ` Michal Pecio
2026-04-01 19:27 ` Mathias Nyman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox