[PATCH 1/2] usbip: drain remaining PDU payload on rejected endpoint

From: Sascha Grunert

Date: Wed Jul 01 2026 - 06:31:13 EST


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@xxxxxxxxxxxxxxx
Signed-off-by: Sascha Grunert <sgrunert@xxxxxxxxxx>
---
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