aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-08-27 14:36:24 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-10-05 09:32:51 -0700
commit1eb1b69f40b9ee5f5645109f380be78daf8e9ddd (patch)
tree98250e421fd383c24e86323fbb2952115024ba0b
parent851834e4a2bd1015b05b201aff79adb643d97f10 (diff)
downloadlinux-jz47xx-1eb1b69f40b9ee5f5645109f380be78daf8e9ddd.tar.gz
USB: xhci: Check URB's actual transfer buffer size.
commit 99eb32db45061443ab7552b8fdceae68b90fde55 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/xhci-ring.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 4142c04b5ad..b4fab00105d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1092,7 +1092,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td->urb->actual_length =
td->urb->transfer_buffer_length -
TRB_LEN(event->transfer_len);
- if (td->urb->actual_length < 0) {
+ if (td->urb->transfer_buffer_length <
+ td->urb->actual_length) {
xhci_warn(xhci, "HC gave bad length "
"of %d bytes left\n",
TRB_LEN(event->transfer_len));
@@ -1167,6 +1168,20 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td_cleanup:
/* Clean up the endpoint's TD list */
urb = td->urb;
+ /* Do one last check of the actual transfer length.
+ * If the host controller said we transferred more data than
+ * the buffer length, urb->actual_length will be a very big
+ * number (since it's unsigned). Play it safe and say we didn't
+ * transfer anything.
+ */
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ xhci_warn(xhci, "URB transfer length is wrong, "
+ "xHC issue? req. len = %u, "
+ "act. len = %u\n",
+ urb->transfer_buffer_length,
+ urb->actual_length);
+ urb->actual_length = 0;
+ }
list_del(&td->td_list);
/* Was this TD slated to be cancelled but completed anyway? */
if (!list_empty(&td->cancelled_td_list)) {