Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: usbip: fix OOB read/write in usbip_pad_iso()

usbip_pad_iso() repositions ISO frame data within the transfer buffer
via memmove(). Neither the source offset (actualoffset, derived by
subtracting wire-supplied actual_length values) nor the destination
offset (iso_frame_desc[i].offset, taken directly from the wire) is
bounds-checked.

If a crafted actual_length wraps actualoffset negative through the
subtraction (see patch 2/3 for the root cause), the memmove source
points before the allocation - slab OOB read, data returned to
userspace.

Independently, iso_frame_desc[i].offset is never validated against
transfer_buffer_length. Setting offset past the end of the buffer
gives a fully controlled OOB write into whatever sits next in the
slab - confirmed with offset=400 on a 392-byte buffer, 64-byte write.

Add bounds checks for both the source and destination ranges before
each memmove call. Use unsigned comparisons after the sign check on
actualoffset to avoid signed/unsigned conversion surprises.

Signed-off-by: Kelvin Mbogo <addcontent08@gmail.com>
Link: https://patch.msgid.link/20260325103640.8090-3-addcontent08@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kelvin Mbogo and committed by
Greg Kroah-Hartman
74a22872 591c1d97

+36
+36
drivers/usb/usbip/usbip_common.c
··· 770 770 */ 771 771 for (i = np-1; i > 0; i--) { 772 772 actualoffset -= urb->iso_frame_desc[i].actual_length; 773 + 774 + /* 775 + * Validate source range: actualoffset can go negative 776 + * via crafted actual_length values from the wire. 777 + */ 778 + if (actualoffset < 0 || 779 + (unsigned int)actualoffset > 780 + (unsigned int)urb->transfer_buffer_length || 781 + urb->iso_frame_desc[i].actual_length > 782 + (unsigned int)urb->transfer_buffer_length - 783 + (unsigned int)actualoffset) { 784 + dev_err(&urb->dev->dev, 785 + "pad_iso: bad src off=%d len=%u bufsz=%d\n", 786 + actualoffset, 787 + urb->iso_frame_desc[i].actual_length, 788 + urb->transfer_buffer_length); 789 + return; 790 + } 791 + 792 + /* 793 + * Validate destination range: iso_frame_desc[i].offset 794 + * is wire-supplied and must not exceed the buffer. 795 + */ 796 + if (urb->iso_frame_desc[i].offset > 797 + (unsigned int)urb->transfer_buffer_length || 798 + urb->iso_frame_desc[i].actual_length > 799 + (unsigned int)urb->transfer_buffer_length - 800 + urb->iso_frame_desc[i].offset) { 801 + dev_err(&urb->dev->dev, 802 + "pad_iso: bad dst off=%u len=%u bufsz=%d\n", 803 + urb->iso_frame_desc[i].offset, 804 + urb->iso_frame_desc[i].actual_length, 805 + urb->transfer_buffer_length); 806 + return; 807 + } 808 + 773 809 memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, 774 810 urb->transfer_buffer + actualoffset, 775 811 urb->iso_frame_desc[i].actual_length);