From: Kelvin Mbogo Date: Wed, 25 Mar 2026 10:36:39 +0000 (+0300) Subject: usb: usbip: validate iso frame actual_length in usbip_recv_iso() X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=591c1d972d8f19862ecd7279c7ef4df48b0a9b33;p=thirdparty%2Fkernel%2Fstable.git usb: usbip: validate iso frame actual_length in usbip_recv_iso() usbip_recv_iso() sums each frame's actual_length into an int accumulator without checking the individual values first: total_length += urb->iso_frame_desc[i].actual_length; A malicious server can send actual_length = 0xFFFFFFFC for one frame and a small value for the other, making the signed sum wrap around to match urb->actual_length. The sanity check passes, and usbip_pad_iso() later computes a negative actualoffset, feeding it to memmove() as a source pointer - reads before the allocation, leaked to userspace via USBDEVFS_REAPURB. Reject any frame whose actual_length exceeds transfer_buffer_length (one frame can't carry more data than the whole buffer), and widen the accumulator to u32 so that many moderately-large frames can't wrap it either. Signed-off-by: Kelvin Mbogo Link: https://patch.msgid.link/20260325103640.8090-2-addcontent08@gmail.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 8b6eb7476747..fd620e960039 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -665,7 +665,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) int size; int i; int ret; - int total_length = 0; + u32 total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; @@ -706,14 +706,23 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) for (i = 0; i < np; i++) { usbip_iso_packet_correct_endian(&iso[i], 0); usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + if (urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length) { + dev_err(&urb->dev->dev, + "recv iso: frame actual_length %u exceeds buffer %d\n", + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + kfree(buff); + return -EPROTO; + } total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); - if (total_length != urb->actual_length) { + if (total_length != (u32)urb->actual_length) { dev_err(&urb->dev->dev, - "total length of iso packets %d not equal to actual length of buffer %d\n", + "total length of iso packets %u not equal to actual length of buffer %d\n", total_length, urb->actual_length); if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC)