diff options
author | Jack Pham <jackp@codeaurora.org> | 2017-06-01 17:57:14 -0700 |
---|---|---|
committer | Jack Pham <jackp@codeaurora.org> | 2017-06-02 09:59:15 -0700 |
commit | a4253a4598b6632a2e49f17fac5ed10abf43aae2 (patch) | |
tree | b27ee134fc2bf0df7ad5e2b1d97908340cac3766 /drivers/usb | |
parent | 28bf2d8b6bd153bb3a27ec871115d76f6a94b97e (diff) |
usb: xhci: Avoid accessing dangling virt_dev in xhci_stop_device
In xhci_stop_device() the virt_dev pointer is assigned from the
device slot prior to acquiring xhci->lock and disabling IRQs.
This is an opportunity for a TRB_DISABLE_SLOT command to have
completed (e.g. in case the device was disconnected, or the port
was disabled) which would have called xhci_free_virt_device()
and freed the slot's pointer in IRQ context. Then after the
lock is acquired virt_dev may now be pointing to freed memory
and results in a panic.
Avoid this by moving the assignment
virt_dev = xhci->devs[slot_id];
and NULL pointer check to after the lock is taken to ensure that
we don't race against xhci IRQ handler.
Change-Id: I4ac23fbfd3def0ad93967316b62c0c0cd19ca33f
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d885033d3322..6c414f252291 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -376,10 +376,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) int i; ret = 0; - virt_dev = xhci->devs[slot_id]; - if (!virt_dev) - return -ENODEV; - cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); if (!cmd) { xhci_dbg(xhci, "Couldn't allocate command structure.\n"); @@ -387,6 +383,13 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) } spin_lock_irqsave(&xhci->lock, flags); + virt_dev = xhci->devs[slot_id]; + if (!virt_dev) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cmd); + return -ENODEV; + } + for (i = LAST_EP_INDEX; i > 0; i--) { if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { struct xhci_command *command; |