usb: gadget: composite: don't queue OS desc req if length is invalid
authorWilliam Wu <william.wu@rock-chips.com>
Fri, 13 May 2016 10:30:42 +0000 (18:30 +0800)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 31 May 2016 08:24:31 +0000 (11:24 +0300)
In OS descriptors handling, if ctrl->bRequestType is
USB_RECIP_DEVICE and w_index != 0x4 or (w_value >> 8)
is true, it will not assign a valid value to req->length,
but use the default value(-EOPNOTSUPP), and queue an
OS desc request with the invalid req->length. It always
happens on the platforms which use os_desc (for example:
rk3366, rk3399), and cause kernel panic as follows
(use dwc3 driver):

Unable to handle kernel paging request at virtual address ffffffc0f7e00000
Internal error: Oops: 96000146 [#1] PREEMPT SMP
PC is at __dma_clean_range+0x18/0x30
LR is at __swiotlb_map_page+0x50/0x64
Call trace:
[<ffffffc0000930f8>] __dma_clean_range+0x18/0x30
[<ffffffc00062214c>] usb_gadget_map_request+0x134/0x1b0
[<ffffffc0005c289c>] __dwc3_ep0_do_control_data+0x110/0x14c
[<ffffffc0005c2d38>] __dwc3_gadget_ep0_queue+0x198/0x1b8
[<ffffffc0005c2e18>] dwc3_gadget_ep0_queue+0xc0/0xe8
[<ffffffc00061cfec>] composite_ep0_queue.constprop.14+0x34/0x98
[<ffffffc00061dfb0>] composite_setup+0xf60/0x100c
[<ffffffc0006204dc>] android_setup+0xd8/0x138
[<ffffffc0005c29a4>] dwc3_ep0_delegate_req+0x34/0x50
[<ffffffc0005c3534>] dwc3_ep0_interrupt+0x5dc/0xb58
[<ffffffc0005c0c3c>] dwc3_thread_interrupt+0x15c/0xa24

With this patch, the gadget driver will not queue
a request and return immediately if req->length is
invalid. And the usb controller driver can handle
the unsupport request correctly.

Signed-off-by: William Wu <william.wu@rock-chips.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/gadget/composite.c

index d67de0d..eb64848 100644 (file)
@@ -1868,14 +1868,19 @@ unknown:
                                }
                                break;
                        }
-                       req->length = value;
-                       req->context = cdev;
-                       req->zero = value < w_length;
-                       value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
-                       if (value < 0) {
-                               DBG(cdev, "ep_queue --> %d\n", value);
-                               req->status = 0;
-                               composite_setup_complete(gadget->ep0, req);
+
+                       if (value >= 0) {
+                               req->length = value;
+                               req->context = cdev;
+                               req->zero = value < w_length;
+                               value = composite_ep0_queue(cdev, req,
+                                                           GFP_ATOMIC);
+                               if (value < 0) {
+                                       DBG(cdev, "ep_queue --> %d\n", value);
+                                       req->status = 0;
+                                       composite_setup_complete(gadget->ep0,
+                                                                req);
+                               }
                        }
                        return value;
                }