On 24.7.2019 17.34, Maik Stohn wrote:

Am 24.07.2019 um 16:20 schrieb Greg KH <gre...@linuxfoundation.org>:

On Wed, Jul 24, 2019 at 03:27:51PM +0200, Maik Stohn wrote:
KERNEL CRASH when using XHCI devices (affects any architecture, any USB device)

This was already reported as a kernel bug in bugzilla 
(https://bugzilla.kernel.org/show_bug.cgi?id=204257) but I got told to report 
it here since it is usb related...

Affected kernels: 5.2, 5.2.1, 5.2.2, 5.3-rc1, ...

This bug is already causing real world problems with existing software and 
devices using SCSI BOT with raw SCSI commands and libusb software.

Reproduce (tested on several different machines with 5.2,5.2.1,5.2.2,5.3rc1):

- usb flash drive attached to XHCI controller (e.g. USB3.0 flash drive attached 
to USB3.0 port)
- generic scsi module loaded (e.g. /dev/sg0 comes up when attaching the flash 
drive)
- command line tool "sg_raw" from "sg3-utils"
- execute: and press a key + return (-s1 sends one byte which is read from 
stdin)
   $ sudo sg_raw -s1 /dev/sg0 00 00 00 00 00 00 00 00 00 00

-> KERNEL Oops

- same for -s2, -s3, ... up to -s8   (sending 1 to 8 bytes, exactly the maximum of bytes 
on my 64 bit machine where the "DMA bypass optimization / IDT" kicks in, see 
below)

Since this can be triggered by any normal user (without any special USB device 
needed) I think it is important enough to fix it for the existing 5.2 kernel as 
well.

---

Patch introducing the crash:  https://patchwork.kernel.org/patch/10919167 / commit 
33e39350ebd20fe6a77a51b8c21c3aa6b4a208cf - "usb: xhci: add Immediate Data Transfer 
support"

Reason: NULL pointer dereference

---

I took me quite some time to find the cause of this.

I narrowed the crash down to the place inside of "xhci_queue_bulk_tx" in 
"xhci-ring.c" where the next SG is loaded

...
    while (sg && sent_len >= block_len) {
      /* New sg entry */
      --num_sgs;
      sent_len -= block_len;
      if (num_sgs != 0) {
        sg = sg_next(sg);
        block_len = sg_dma_len(sg);           <================= CRASH
                                                                 The comment of 
"sg_dma_len" clearly states "These macros should be used after a dma_map_sg call has 
been done..." - which was
                                                                 omitted by the new 
"xhci_map_urb_for_dma" function since the transfer was considered suitable for 
IDT.
        addr = (u64) sg_dma_address(sg);
        addr += sent_len;
      }
    }
    block_len -= sent_len;
    send_addr = addr;
...

This only happens if the transfer was cosnideres suitable for IDT.
When I patched the function "xhci_urb_suitable_for_idt" to always return false 
(nothing suitable for IDT) everything was working fine.


Unfortunately I'm not deep enough into the inner workings of the kernel usb 
host driver to find a solution for this other than reverting the patch for IDT.

Nice catch.
The immediate data (IDT) support assumed that when there is max 8 bytes of data,
and it's not already dma mapped then we can just copy the data directly from
urb->transfer_buffer.
                                                                                
        
I didn't take into account that this small amount of data can be in a sg list.

Does the below code help:

diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 7a26496..f5c4144 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2175,7 +2175,8 @@ static inline bool xhci_urb_suitable_for_idt(struct urb 
*urb)
        if (!usb_endpoint_xfer_isoc(&urb->ep->desc) && usb_urb_dir_out(urb) &&
            usb_endpoint_maxp(&urb->ep->desc) >= TRB_IDT_MAX_SIZE &&
            urb->transfer_buffer_length <= TRB_IDT_MAX_SIZE &&
-           !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+           !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) &&
+           !urb->num_sgs)
                return true;
return false;


-Mathias

Reply via email to