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

The commit 49c45e0b44f4 ("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. And also compare
the count value with the event buffer length to
prevent out of bound memory access.

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: Sriharsha Allenki <sallenki@codeaurora.org>
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 281fb40..2426ea9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2098,16 +2098,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 		 */
 		dwc3_stop_active_transfers(dwc);
 
-		/*
-		 * Clear out any pending events (i.e. End Transfer Command
-		 * Complete) before clearing run/stop
-		 */
-		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)
@@ -2116,6 +2106,19 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
 	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).
+		 */
+		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);
+	}
+
 	do {
 		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 		reg &= DWC3_DSTS_DEVCTRLHLT;
@@ -3748,6 +3751,12 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
 	if (dwc->err_evt_seen)
 		return IRQ_HANDLED;
 
+	/* 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
@@ -3762,6 +3771,19 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
 	if (!count)
 		return IRQ_NONE;
 
+	if (count > evt->length) {
+		dbg_event(0xFF, "HUGE_EVCNT", count);
+		/*
+		 * If writes from dwc3_interrupt and run_stop(0) races
+		 * with each other, the count can result in a very large
+		 * value.In that case setting the evt->lpos here
+		 * is a no-op. The value will be reset as part of run_stop(1).
+		 */
+		evt->lpos = (evt->lpos + count) % DWC3_EVENT_BUFFERS_SIZE;
+		dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+		return IRQ_HANDLED;
+	}
+
 	evt->count = count;
 	evt->flags |= DWC3_EVENT_PENDING;