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

Commit 0973f9fe authored by Sriharsha Allenki's avatar Sriharsha Allenki Committed by Gerrit - the friendly Code Review server
Browse files

usb: dwc3: Fix out of bound memory access for event buffer



The commit 323f5aa0f325 ("usb: dwc3: gadget: Clear
pending events when stopping controller") added a
race of writing to the GEVNTCOUNT between the run_stop
and the dwc3_check_event_buf. This causes the
GEVNTCOUNT to be decremented below zero by the controller
and is resulting in a huge values(0xFFFC) which is much
larger than the event buffer size(0x1000).
When this happens the next dwc3_interrupt will be accessing
the next page after the event buffer resulting in a
memory abort.
Fix this by discarding any interrupts that are fired
after the run_stop bit is cleared.

The earlier commit still leaves a window of an event being
generated by the controller between clearing the pending
events and clearing the run_stop bit preventing the controller
from being halted. Fix this by clearing the pending events
after the run_stop bit is cleared.

Change-Id: Ic5244485dc1af728848f40c45f920a6a6f880ac2
Signed-off-by: default avatarSriharsha Allenki <sallenki@codeaurora.org>
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent 5d86ce27
Loading
Loading
Loading
Loading
+17 −8
Original line number Diff line number Diff line
@@ -2046,24 +2046,27 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
		 */
		dwc3_stop_active_transfers(dwc);

		reg &= ~DWC3_DCTL_RUN_STOP;

		if (dwc->has_hibernation && !suspend)
			reg &= ~DWC3_DCTL_KEEP_CONNECT;
	}

	dwc3_writel(dwc->regs, DWC3_DCTL, reg);

	/* Controller is not halted until the events are acknowledged */
	if (!is_on) {
		/*
		 * Clear out any pending events (i.e. End Transfer Command
		 * Complete) before clearing run/stop
		 * Complete).
		 */
		reg1 = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
		reg1 &= DWC3_GEVNTCOUNT_MASK;
		dbg_log_string("remaining EVNTCOUNT(0)=%d", reg1);
		dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg1);
		dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEAR, 0);

		reg &= ~DWC3_DCTL_RUN_STOP;

		if (dwc->has_hibernation && !suspend)
			reg &= ~DWC3_DCTL_KEEP_CONNECT;
	}

	dwc3_writel(dwc->regs, DWC3_DCTL, reg);

	do {
		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
		reg &= DWC3_DSTS_DEVCTRLHLT;
@@ -3775,6 +3778,12 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc)

	evt = dwc->ev_buf;

	/* Controller is being halted, ignore the interrupts */
	if (!dwc->pullups_connected) {
		dbg_event(0xFF, "NO_PULLUP", 0);
		return IRQ_HANDLED;
	}

	/*
	 * With PCIe legacy interrupt, test shows that top-half irq handler can
	 * be called again after HW interrupt deassertion. Check if bottom-half