usb: dwc2: host: spinlock urb_enqueue
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>
This commit is contained in:
parent
a7714c1cb1
commit
33ad261aa6
@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
|
|||||||
writel(0, hsotg->regs + HPRT0);
|
writel(0, hsotg->regs + HPRT0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Caller must hold driver lock */
|
||||||
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
||||||
struct dwc2_hcd_urb *urb, void **ep_handle,
|
struct dwc2_hcd_urb *urb, void **ep_handle,
|
||||||
gfp_t mem_flags)
|
gfp_t mem_flags)
|
||||||
{
|
{
|
||||||
struct dwc2_qtd *qtd;
|
struct dwc2_qtd *qtd;
|
||||||
unsigned long flags;
|
|
||||||
u32 intr_mask;
|
u32 intr_mask;
|
||||||
int retval;
|
int retval;
|
||||||
int dev_speed;
|
int dev_speed;
|
||||||
@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
|||||||
*/
|
*/
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&hsotg->lock, flags);
|
|
||||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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",
|
"%s: unaligned transfer with no transfer_buffer",
|
||||||
__func__);
|
__func__);
|
||||||
retval = -EINVAL;
|
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);
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
if (retval)
|
if (retval)
|
||||||
goto fail1;
|
goto fail1;
|
||||||
|
|
||||||
@ -2521,22 +2518,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
|||||||
goto fail2;
|
goto fail2;
|
||||||
|
|
||||||
if (alloc_bandwidth) {
|
if (alloc_bandwidth) {
|
||||||
spin_lock_irqsave(&hsotg->lock, flags);
|
|
||||||
dwc2_allocate_bus_bandwidth(hcd,
|
dwc2_allocate_bus_bandwidth(hcd,
|
||||||
dwc2_hcd_get_ep_bandwidth(hsotg, ep),
|
dwc2_hcd_get_ep_bandwidth(hsotg, ep),
|
||||||
urb);
|
urb);
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail2:
|
fail2:
|
||||||
spin_lock_irqsave(&hsotg->lock, flags);
|
|
||||||
dwc2_urb->priv = NULL;
|
dwc2_urb->priv = NULL;
|
||||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
fail1:
|
fail1:
|
||||||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||||
urb->hcpriv = NULL;
|
urb->hcpriv = NULL;
|
||||||
|
fail0:
|
||||||
kfree(dwc2_urb);
|
kfree(dwc2_urb);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -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
|
* dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
|
||||||
|
* Caller must hold driver lock.
|
||||||
*
|
*
|
||||||
* @hsotg: The DWC HCD structure
|
* @hsotg: The DWC HCD structure
|
||||||
* @qtd: The QTD to add
|
* @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_qh **qh, gfp_t mem_flags)
|
||||||
{
|
{
|
||||||
struct dwc2_hcd_urb *urb = qtd->urb;
|
struct dwc2_hcd_urb *urb = qtd->urb;
|
||||||
unsigned long flags;
|
|
||||||
int allocated = 0;
|
int allocated = 0;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
@ -792,15 +792,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
|||||||
allocated = 1;
|
allocated = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&hsotg->lock, flags);
|
|
||||||
|
|
||||||
retval = dwc2_hcd_qh_add(hsotg, *qh);
|
retval = dwc2_hcd_qh_add(hsotg, *qh);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
qtd->qh = *qh;
|
qtd->qh = *qh;
|
||||||
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
|
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -817,10 +814,7 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
|||||||
qtd_list_entry)
|
qtd_list_entry)
|
||||||
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
|
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
dwc2_hcd_qh_free(hsotg, qh_tmp);
|
dwc2_hcd_qh_free(hsotg, qh_tmp);
|
||||||
} else {
|
|
||||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
Loading…
Reference in New Issue
Block a user