From 1ef8aaf1ef348a22b72ba4367c5e2e5727542f59 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 15 Feb 2013 22:36:47 +0000 Subject: [PATCH 049/196] dwc_otg: Fix unsafe access of QTD during URB enqueue In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the transaction could complete almost immediately after the qtd was assigned to a host channel during URB enqueue, which meant the qtd pointer was no longer valid having been completed and removed. Usually, this resulted in an OOPS during URB submission. By predetermining whether transactions need to be queued or not, this unsafe pointer access is avoided. This bug was only evident on the Pi model A where a device was attached that had no periodic endpoints (e.g. USB pendrive or some wlan devices). --- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 23 ++++++++++++----------- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c index d5c94f4..e653d84 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -462,6 +462,8 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, { dwc_irqflags_t flags; int retval = 0; + uint8_t needs_scheduling = 0; + dwc_otg_transaction_type_e tr_type; dwc_otg_qtd_t *qtd; gintmsk_data_t intr_mask = {.d32 = 0 }; @@ -493,22 +495,22 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, return -DWC_E_NO_MEMORY; } #endif - retval = - dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); + intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); + if(!intr_mask.b.sofintr) needs_scheduling = 1; + if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) + /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ + needs_scheduling = 0; + + retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); // creates a new queue in ep_handle if it doesn't exist already if (retval < 0) { DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " "Error status %d\n", retval); dwc_otg_hcd_qtd_free(qtd); + return retval; } - intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); - if (!intr_mask.b.sofintr && retval == 0) { - dwc_otg_transaction_type_e tr_type; - if ((qtd->qh->ep_type == UE_BULK) - && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { - /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ - return 0; - } + + if(needs_scheduling) { DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); tr_type = dwc_otg_hcd_select_transactions(hcd); if (tr_type != DWC_OTG_TRANSACTION_NONE) { @@ -516,7 +518,6 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, } DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); } - return retval; } diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c index b337e1b..b3e6e52 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c @@ -937,7 +937,7 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, if (*qh == NULL) { *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); if (*qh == NULL) { - retval = -1; + retval = -DWC_E_NO_MEMORY; goto done; } } -- 1.9.1