* [PATCH 0/2] usbip: fix device disconnect loop with isoc endpoints
@ 2026-07-01 10:18 Sascha Grunert
2026-07-01 10:18 ` [PATCH 1/2] usbip: drain remaining PDU payload on rejected endpoint Sascha Grunert
2026-07-01 10:18 ` [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings Sascha Grunert
0 siblings, 2 replies; 5+ messages in thread
From: Sascha Grunert @ 2026-07-01 10:18 UTC (permalink / raw)
To: linux-usb
Cc: valentina.manea.m, shuah, i, gregkh, linux-kernel, stable,
Sascha Grunert
Forwarding a USB device with isochronous endpoints over USB/IP causes a
disconnect/reconnect loop. Hit this with a Turtle Beach Velocity One
Flight yoke (10f5:7001) forwarded to a VM.
The first patch fixes a TCP stream desync: when get_pipe() returns -1,
the remaining PDU payload stays on the socket and corrupts the next
header parse. The second patch prevents activation of alt settings with
isoc endpoints, since USB/IP cannot forward them and the failed
transfers cascade into a device disconnect.
Sascha Grunert (2):
usbip: drain remaining PDU payload on rejected endpoint
usbip: block SET_INTERFACE for isoc alt settings
drivers/usb/usbip/stub_rx.c | 96 ++++++++++++++++++++++++++++++++++++-
1 file changed, 95 insertions(+), 1 deletion(-)
--
2.52.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] usbip: drain remaining PDU payload on rejected endpoint
2026-07-01 10:18 [PATCH 0/2] usbip: fix device disconnect loop with isoc endpoints Sascha Grunert
@ 2026-07-01 10:18 ` Sascha Grunert
2026-07-01 10:18 ` [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings Sascha Grunert
1 sibling, 0 replies; 5+ messages in thread
From: Sascha Grunert @ 2026-07-01 10:18 UTC (permalink / raw)
To: linux-usb
Cc: valentina.manea.m, shuah, i, gregkh, linux-kernel, stable,
Sascha Grunert
When get_pipe() returns -1, stub_recv_cmd_submit() bails out without
reading the transfer buffer and ISO descriptors that follow the PDU
header on the TCP stream. The next recv() parses leftover payload as a
PDU header, desyncs the stream, and kills the connection.
Consume those trailing bytes before the early return so the stream
stays in sync.
Fixes: 635f545a7e8b ("usbip: fix stub_rx: get_pipe() to validate endpoint number")
Cc: stable@vger.kernel.org
Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
---
drivers/usb/usbip/stub_rx.c | 60 ++++++++++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 1e9ae57..d0e3d3f 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -461,6 +461,62 @@ static int stub_recv_xbuff(struct usbip_device *ud, struct stub_priv *priv)
return ret;
}
+/*
+ * When get_pipe() rejects an endpoint (e.g. an isochronous endpoint that
+ * does not exist in the current alt setting), the transfer buffer and ISO
+ * packet descriptors that follow the PDU header on the TCP stream must
+ * still be consumed. Without this the next recv() interprets leftover
+ * payload bytes as a PDU header, desynchronises the stream, and tears
+ * down the connection.
+ */
+static void stub_recv_cmd_submit_drain(struct usbip_device *ud,
+ struct usbip_header *pdu)
+{
+ int bufsz, ret, np;
+ void *buf;
+
+ if (pdu->base.direction == USBIP_DIR_OUT) {
+ bufsz = pdu->u.cmd_submit.transfer_buffer_length;
+ if (bufsz > 0) {
+ buf = kzalloc(min_t(int, bufsz, PAGE_SIZE),
+ GFP_KERNEL);
+ if (!buf) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+ while (bufsz > 0) {
+ int chunk = min_t(int, bufsz, PAGE_SIZE);
+
+ ret = usbip_recv(ud->tcp_socket, buf, chunk);
+ if (ret != chunk) {
+ kfree(buf);
+ usbip_event_add(ud,
+ SDEV_EVENT_ERROR_TCP);
+ return;
+ }
+ bufsz -= chunk;
+ }
+ kfree(buf);
+ }
+ }
+
+ np = pdu->u.cmd_submit.number_of_packets;
+ if (np > 0 && np <= USBIP_MAX_ISO_PACKETS) {
+ bufsz = np * sizeof(struct usbip_iso_packet_descriptor);
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+ ret = usbip_recv(ud->tcp_socket, buf, bufsz);
+ kfree(buf);
+ if (ret != bufsz) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ return;
+ }
+ }
+}
+
static void stub_recv_cmd_submit(struct stub_device *sdev,
struct usbip_header *pdu)
{
@@ -479,8 +535,10 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
int ret, i;
int is_tweaked;
- if (pipe == -1)
+ if (pipe == -1) {
+ stub_recv_cmd_submit_drain(ud, pdu);
return;
+ }
/*
* Smatch reported the error case where use_sg is true and buf_len is 0.
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings
2026-07-01 10:18 [PATCH 0/2] usbip: fix device disconnect loop with isoc endpoints Sascha Grunert
2026-07-01 10:18 ` [PATCH 1/2] usbip: drain remaining PDU payload on rejected endpoint Sascha Grunert
@ 2026-07-01 10:18 ` Sascha Grunert
2026-07-01 10:25 ` Greg KH
1 sibling, 1 reply; 5+ messages in thread
From: Sascha Grunert @ 2026-07-01 10:18 UTC (permalink / raw)
To: linux-usb
Cc: valentina.manea.m, shuah, i, gregkh, linux-kernel, stable,
Sascha Grunert
USB/IP cannot forward isochronous transfers. When the client activates
an alt setting with isoc endpoints, the transfers fail with -EPROTO and
the resulting usb_clear_halt cascade disconnects the device.
Intercept SET_INTERFACE in tweak_set_interface_cmd() and fake success
when the target alt setting contains isoc endpoints, keeping the device
at alt 0.
Tested with a Turtle Beach Velocity One Flight yoke (10f5:7001)
forwarded to a VM via USB/IP, which previously disconnect-looped every
few seconds and now stays connected.
Cc: stable@vger.kernel.org
Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
---
drivers/usb/usbip/stub_rx.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index d0e3d3f..f323b48 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -100,6 +100,28 @@ static int tweak_clear_halt_cmd(struct urb *urb)
return ret;
}
+static bool altsetting_has_isoc(struct usb_device *udev, __u16 interface,
+ __u16 alternate)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *alt;
+ int i;
+
+ intf = usb_ifnum_to_if(udev, interface);
+ if (!intf)
+ return false;
+
+ alt = usb_altnum_to_altsetting(intf, alternate);
+ if (!alt)
+ return false;
+
+ for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+ if (usb_endpoint_xfer_isoc(&alt->endpoint[i].desc))
+ return true;
+ }
+ return false;
+}
+
static int tweak_set_interface_cmd(struct urb *urb)
{
struct usb_ctrlrequest *req;
@@ -111,6 +133,20 @@ static int tweak_set_interface_cmd(struct urb *urb)
alternate = le16_to_cpu(req->wValue);
interface = le16_to_cpu(req->wIndex);
+ /*
+ * USB/IP cannot forward isochronous transfers. If the requested
+ * alt setting activates isochronous endpoints, pretend the switch
+ * succeeded without touching the device. This prevents the
+ * cascade of failed isoc URBs that leads to a device disconnect.
+ */
+ if (alternate != 0 && altsetting_has_isoc(urb->dev, interface,
+ alternate)) {
+ dev_info(&urb->dev->dev,
+ "usb_set_interface blocked: inf %u alt %u (isoc)\n",
+ interface, alternate);
+ return 0;
+ }
+
usbip_dbg_stub_rx("set_interface: inf %u alt %u\n",
interface, alternate);
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings
2026-07-01 10:18 ` [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings Sascha Grunert
@ 2026-07-01 10:25 ` Greg KH
2026-07-01 12:06 ` Sascha Grunert
0 siblings, 1 reply; 5+ messages in thread
From: Greg KH @ 2026-07-01 10:25 UTC (permalink / raw)
To: Sascha Grunert
Cc: linux-usb, valentina.manea.m, shuah, i, linux-kernel, stable
On Wed, Jul 01, 2026 at 12:18:26PM +0200, Sascha Grunert wrote:
> USB/IP cannot forward isochronous transfers. When the client activates
> an alt setting with isoc endpoints, the transfers fail with -EPROTO and
> the resulting usb_clear_halt cascade disconnects the device.
>
> Intercept SET_INTERFACE in tweak_set_interface_cmd() and fake success
> when the target alt setting contains isoc endpoints, keeping the device
> at alt 0.
>
> Tested with a Turtle Beach Velocity One Flight yoke (10f5:7001)
> forwarded to a VM via USB/IP, which previously disconnect-looped every
> few seconds and now stays connected.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
What commit id does this fix?
> ---
> drivers/usb/usbip/stub_rx.c | 36 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
>
> diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
> index d0e3d3f..f323b48 100644
> --- a/drivers/usb/usbip/stub_rx.c
> +++ b/drivers/usb/usbip/stub_rx.c
> @@ -100,6 +100,28 @@ static int tweak_clear_halt_cmd(struct urb *urb)
> return ret;
> }
>
> +static bool altsetting_has_isoc(struct usb_device *udev, __u16 interface,
> + __u16 alternate)
> +{
> + struct usb_interface *intf;
> + struct usb_host_interface *alt;
> + int i;
> +
> + intf = usb_ifnum_to_if(udev, interface);
> + if (!intf)
> + return false;
> +
> + alt = usb_altnum_to_altsetting(intf, alternate);
> + if (!alt)
> + return false;
> +
> + for (i = 0; i < alt->desc.bNumEndpoints; i++) {
> + if (usb_endpoint_xfer_isoc(&alt->endpoint[i].desc))
> + return true;
> + }
> + return false;
> +}
> +
> static int tweak_set_interface_cmd(struct urb *urb)
> {
> struct usb_ctrlrequest *req;
> @@ -111,6 +133,20 @@ static int tweak_set_interface_cmd(struct urb *urb)
> alternate = le16_to_cpu(req->wValue);
> interface = le16_to_cpu(req->wIndex);
>
> + /*
> + * USB/IP cannot forward isochronous transfers. If the requested
> + * alt setting activates isochronous endpoints, pretend the switch
> + * succeeded without touching the device. This prevents the
> + * cascade of failed isoc URBs that leads to a device disconnect.
> + */
> + if (alternate != 0 && altsetting_has_isoc(urb->dev, interface,
> + alternate)) {
> + dev_info(&urb->dev->dev,
> + "usb_set_interface blocked: inf %u alt %u (isoc)\n",
> + interface, alternate);
Why is this not an error? And if a user sees this, what can they do
about it?
> + return 0;
Why isn't this an error?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings
2026-07-01 10:25 ` Greg KH
@ 2026-07-01 12:06 ` Sascha Grunert
0 siblings, 0 replies; 5+ messages in thread
From: Sascha Grunert @ 2026-07-01 12:06 UTC (permalink / raw)
To: Greg KH; +Cc: linux-usb, valentina.manea.m, shuah, i, linux-kernel, stable
On Wed, Jul 01, 2026, Greg Kroah-Hartman wrote:
> What commit id does this fix?
The limitation goes back to the original import:
Fixes: 4d7b5c7f8ad4 ("Staging: USB/IP: add host driver")
tweak_set_interface_cmd() has always forwarded SET_INTERFACE
unconditionally, which is fine until an alt setting with isochronous
endpoints is activated.
> Why is this not an error? And if a user sees this, what can they do
> about it?
>
> > + return 0;
>
> Why isn't this an error?
Returning 0 here means "handled" in tweak_special_requests() semantics
(!err = 1 = tweaked), so the URB is completed with success and never
submitted to the HCD. If we returned an error instead,
tweak_special_requests() would return 0 (not tweaked) and the URB
goes through to the hardware, the isoc alt setting activates, the
transfers fail, and the device disconnects, which is the problem
this patch is trying to prevent.
You're right that the user can't act on the log message though. I'll
change dev_info to dev_dbg in v2.
Thanks,
Sascha
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-01 12:07 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 10:18 [PATCH 0/2] usbip: fix device disconnect loop with isoc endpoints Sascha Grunert
2026-07-01 10:18 ` [PATCH 1/2] usbip: drain remaining PDU payload on rejected endpoint Sascha Grunert
2026-07-01 10:18 ` [PATCH 2/2] usbip: block SET_INTERFACE for isoc alt settings Sascha Grunert
2026-07-01 10:25 ` Greg KH
2026-07-01 12:06 ` Sascha Grunert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox