* [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