Commit 02e2c51b authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: fix obscure race in ehci_endpoint_disable

This patch (as1435) fixes an obscure and unlikely race in ehci-hcd.
When an async URB is unlinked, the corresponding QH is removed from
the async list.  If the QH's endpoint is then disabled while the URB
is being given back, ehci_endpoint_disable() won't find the QH on the
async list, causing it to believe that the QH has been lost.  This
will lead to a memory leak at best and quite possibly to an oops.

The solution is to trust usbcore not to lose track of endpoints.  If
the QH isn't on the async list then it doesn't need to be taken off
the list, but the driver should still wait for the QH to become IDLE
before disabling it.

In theory this fixes Bugzilla #20182.  In fact the race is so rare
that it's not possible to tell whether the bug is still present.
However, adding delays and making other changes to force the race
seems to show that the patch works.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
CC: David Brownell <david-b@pacbell.net>
CC: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b4880951
...@@ -1063,10 +1063,11 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -1063,10 +1063,11 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
tmp && tmp != qh; tmp && tmp != qh;
tmp = tmp->qh_next.qh) tmp = tmp->qh_next.qh)
continue; continue;
/* periodic qh self-unlinks on empty */ /* periodic qh self-unlinks on empty, and a COMPLETING qh
if (!tmp) * may already be unlinked.
goto nogood; */
unlink_async (ehci, qh); if (tmp)
unlink_async(ehci, qh);
/* FALL THROUGH */ /* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */ case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT: case QH_STATE_UNLINK_WAIT:
...@@ -1083,7 +1084,6 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -1083,7 +1084,6 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
} }
/* else FALL THROUGH */ /* else FALL THROUGH */
default: default:
nogood:
/* caller was supposed to have unlinked any requests; /* caller was supposed to have unlinked any requests;
* that's not our job. just leak this memory. * that's not our job. just leak this memory.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment