]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: usbip: validate iso frame actual_length in usbip_recv_iso()
authorKelvin Mbogo <addcontent08@gmail.com>
Wed, 25 Mar 2026 10:36:39 +0000 (13:36 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Apr 2026 07:52:51 +0000 (09:52 +0200)
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 <addcontent08@gmail.com>
Link: https://patch.msgid.link/20260325103640.8090-2-addcontent08@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/usbip/usbip_common.c

index 8b6eb74767475b48f210d7c0acd5cc1d77956d32..fd620e96003947a2b8c35a7d5e70067f5f1e9880 100644 (file)
@@ -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)