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));
 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)) {