aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDouglas Anderson <dianders@chromium.org>2016-01-28 21:19:55 -0500
committerFelipe Balbi <balbi@kernel.org>2016-03-04 08:14:40 -0500
commit16e80218816488f016418717d23c660abe073a67 (patch)
tree74dd155a1274bbc7eace92a76e7907c19d7ffddd
parent098c1ef8fe6bcdfed7905cea1debdd3a0ff9a16f (diff)
usb: dwc2: host: Avoid use of chan->qh after qh freed
When poking around with USB devices with slub_debug enabled, I found another obvious use after free. Turns out that in dwc2_hc_n_intr() I was in a state when the contents of chan->qh was filled with 0x6b, indicating that chan->qh was freed but chan still had a reference to it. Let's make sure that whenever we free qh we also make sure we remove a reference from its channel. The bug fixed here doesn't appear to be new--I believe I just got lucky and happened to see it while stress testing. Acked-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Douglas Anderson <dianders@chromium.org> Reviewed-by: Kever Yang <kever.yang@rock-chips.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Stefan Wahren <stefan.wahren@i2se.com> Signed-off-by: Felipe Balbi <balbi@kernel.org>
-rw-r--r--drivers/usb/dwc2/hcd.c10
-rw-r--r--drivers/usb/dwc2/hcd_intr.c10
2 files changed, 20 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index bc4bdbc1534e..e2d2e9be366e 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -164,6 +164,9 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg,
164 qtd_list_entry) 164 qtd_list_entry)
165 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); 165 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
166 166
167 if (qh->channel && qh->channel->qh == qh)
168 qh->channel->qh = NULL;
169
167 spin_unlock_irqrestore(&hsotg->lock, flags); 170 spin_unlock_irqrestore(&hsotg->lock, flags);
168 dwc2_hcd_qh_free(hsotg, qh); 171 dwc2_hcd_qh_free(hsotg, qh);
169 spin_lock_irqsave(&hsotg->lock, flags); 172 spin_lock_irqsave(&hsotg->lock, flags);
@@ -554,7 +557,12 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
554 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); 557 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
555 558
556 ep->hcpriv = NULL; 559 ep->hcpriv = NULL;
560
561 if (qh->channel && qh->channel->qh == qh)
562 qh->channel->qh = NULL;
563
557 spin_unlock_irqrestore(&hsotg->lock, flags); 564 spin_unlock_irqrestore(&hsotg->lock, flags);
565
558 dwc2_hcd_qh_free(hsotg, qh); 566 dwc2_hcd_qh_free(hsotg, qh);
559 567
560 return 0; 568 return 0;
@@ -2782,6 +2790,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
2782fail3: 2790fail3:
2783 dwc2_urb->priv = NULL; 2791 dwc2_urb->priv = NULL;
2784 usb_hcd_unlink_urb_from_ep(hcd, urb); 2792 usb_hcd_unlink_urb_from_ep(hcd, urb);
2793 if (qh_allocated && qh->channel && qh->channel->qh == qh)
2794 qh->channel->qh = NULL;
2785fail2: 2795fail2:
2786 spin_unlock_irqrestore(&hsotg->lock, flags); 2796 spin_unlock_irqrestore(&hsotg->lock, flags);
2787 urb->hcpriv = NULL; 2797 urb->hcpriv = NULL;
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 4270f6c719c6..0d0fd2a7f1f9 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1943,6 +1943,16 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
1943 } 1943 }
1944 1944
1945 dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); 1945 dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
1946
1947 /*
1948 * If we got an interrupt after someone called
1949 * dwc2_hcd_endpoint_disable() we don't want to crash below
1950 */
1951 if (!chan->qh) {
1952 dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
1953 return;
1954 }
1955
1946 chan->hcint = hcint; 1956 chan->hcint = hcint;
1947 hcint &= hcintmsk; 1957 hcint &= hcintmsk;
1948 1958