diff options
author | Douglas Anderson <dianders@chromium.org> | 2016-01-28 21:19:55 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@kernel.org> | 2016-03-04 08:14:40 -0500 |
commit | 16e80218816488f016418717d23c660abe073a67 (patch) | |
tree | 74dd155a1274bbc7eace92a76e7907c19d7ffddd | |
parent | 098c1ef8fe6bcdfed7905cea1debdd3a0ff9a16f (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.c | 10 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd_intr.c | 10 |
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, | |||
2782 | fail3: | 2790 | fail3: |
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; | ||
2785 | fail2: | 2795 | fail2: |
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 | ||