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

USB: xhci: Check URB's actual transfer buffer size.

Make sure that the amount of data the xHC says was transmitted is less
than or equal to the size of the requested transfer buffer. Before, if
the host controller erroneously reported that the number of bytes
untransferred was bigger than the buffer in the URB, urb->actual_length
could be set to a very large size.

Make sure urb->actual_length <= urb->transfer_buffer_length.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Sarah Sharp and committed by
Greg Kroah-Hartman
99eb32db 9191eee7

+16 -1
+16 -1
drivers/usb/host/xhci-ring.c
··· 1092 1092 td->urb->actual_length = 1093 1093 td->urb->transfer_buffer_length - 1094 1094 TRB_LEN(event->transfer_len); 1095 - if (td->urb->actual_length < 0) { 1095 + if (td->urb->transfer_buffer_length < 1096 + td->urb->actual_length) { 1096 1097 xhci_warn(xhci, "HC gave bad length " 1097 1098 "of %d bytes left\n", 1098 1099 TRB_LEN(event->transfer_len)); ··· 1168 1167 td_cleanup: 1169 1168 /* Clean up the endpoint's TD list */ 1170 1169 urb = td->urb; 1170 + /* Do one last check of the actual transfer length. 1171 + * If the host controller said we transferred more data than 1172 + * the buffer length, urb->actual_length will be a very big 1173 + * number (since it's unsigned). Play it safe and say we didn't 1174 + * transfer anything. 1175 + */ 1176 + if (urb->actual_length > urb->transfer_buffer_length) { 1177 + xhci_warn(xhci, "URB transfer length is wrong, " 1178 + "xHC issue? req. len = %u, " 1179 + "act. len = %u\n", 1180 + urb->transfer_buffer_length, 1181 + urb->actual_length); 1182 + urb->actual_length = 0; 1183 + } 1171 1184 list_del(&td->td_list); 1172 1185 /* Was this TD slated to be cancelled but completed anyway? */ 1173 1186 if (!list_empty(&td->cancelled_td_list)) {