Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33ad261a authored by Gregory Herrero's avatar Gregory Herrero Committed by Felipe Balbi
Browse files

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: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarGregory Herrero <gregory.herrero@intel.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent a7714c1c
Loading
Loading
Loading
Loading
+6 −9
Original line number Diff line number Diff line
@@ -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;
+1 −7
Original line number Diff line number Diff line
@@ -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 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
					 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;