usb: dwc2: host: spinlock urb_enqueue
authorGregory Herrero <gregory.herrero@intel.com>
Wed, 29 Apr 2015 20:09:15 +0000 (22:09 +0200)
committerFelipe Balbi <balbi@ti.com>
Wed, 29 Apr 2015 20:19:55 +0000 (15:19 -0500)
During urb_enqueue, if the urb can't be queued to the endpoint,
the urb is freed without any spinlock protection.
This leads to memory corruption when concurrent urb_dequeue try to free
same urb->hcpriv.
Thus, ensure the whole urb_enqueue in spinlocked.

Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Gregory Herrero <gregory.herrero@intel.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc2/hcd.c
drivers/usb/dwc2/hcd_queue.c

index 780f298ec02d3f31094c4bdca19845e54f01dbe9..d72557cfc05280f53e66acf7298fef9c8c578955 100644 (file)
@@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
        writel(0, hsotg->regs + HPRT0);
 }
 
+/* Caller must hold driver lock */
 static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
                                struct dwc2_hcd_urb *urb, void **ep_handle,
                                gfp_t mem_flags)
 {
        struct dwc2_qtd *qtd;
-       unsigned long flags;
        u32 intr_mask;
        int retval;
        int dev_speed;
@@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
                         */
                        return 0;
 
-               spin_lock_irqsave(&hsotg->lock, flags);
                tr_type = dwc2_hcd_select_transactions(hsotg);
                if (tr_type != DWC2_TRANSACTION_NONE)
                        dwc2_hcd_queue_transactions(hsotg, tr_type);
-               spin_unlock_irqrestore(&hsotg->lock, flags);
        }
 
        return 0;
@@ -2484,7 +2482,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
                                "%s: unaligned transfer with no transfer_buffer",
                                __func__);
                        retval = -EINVAL;
-                       goto fail1;
+                       goto fail0;
                }
        }
 
@@ -2512,7 +2510,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 
        spin_lock_irqsave(&hsotg->lock, flags);
        retval = usb_hcd_link_urb_to_ep(hcd, urb);
-       spin_unlock_irqrestore(&hsotg->lock, flags);
        if (retval)
                goto fail1;
 
@@ -2521,22 +2518,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
                goto fail2;
 
        if (alloc_bandwidth) {
-               spin_lock_irqsave(&hsotg->lock, flags);
                dwc2_allocate_bus_bandwidth(hcd,
                                dwc2_hcd_get_ep_bandwidth(hsotg, ep),
                                urb);
-               spin_unlock_irqrestore(&hsotg->lock, flags);
        }
 
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
        return 0;
 
 fail2:
-       spin_lock_irqsave(&hsotg->lock, flags);
        dwc2_urb->priv = NULL;
        usb_hcd_unlink_urb_from_ep(hcd, urb);
-       spin_unlock_irqrestore(&hsotg->lock, flags);
 fail1:
+       spin_unlock_irqrestore(&hsotg->lock, flags);
        urb->hcpriv = NULL;
+fail0:
        kfree(dwc2_urb);
 
        return retval;
index bb97838bc6c04cf1317a837219542291355104da..63207dc3cb22920319af022001495599e04e4398 100644 (file)
@@ -761,6 +761,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
 
 /**
  * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
+ *                     Caller must hold driver lock.
  *
  * @hsotg:        The DWC HCD structure
  * @qtd:          The QTD to add
@@ -777,7 +778,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
                     struct dwc2_qh **qh, gfp_t mem_flags)
 {
        struct dwc2_hcd_urb *urb = qtd->urb;
-       unsigned long flags;
        int allocated = 0;
        int retval;
 
@@ -792,15 +792,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
                allocated = 1;
        }
 
-       spin_lock_irqsave(&hsotg->lock, flags);
-
        retval = dwc2_hcd_qh_add(hsotg, *qh);
        if (retval)
                goto fail;
 
        qtd->qh = *qh;
        list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
-       spin_unlock_irqrestore(&hsotg->lock, flags);
 
        return 0;
 
@@ -817,10 +814,7 @@ fail:
                                         qtd_list_entry)
                        dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
 
-               spin_unlock_irqrestore(&hsotg->lock, flags);
                dwc2_hcd_qh_free(hsotg, qh_tmp);
-       } else {
-               spin_unlock_irqrestore(&hsotg->lock, flags);
        }
 
        return retval;