]> git.itanic.dy.fi Git - linux-stable/commitdiff
usb: xhci: Handle error condition in xhci_stop_device()
authorMayank Rana <mrana@codeaurora.org>
Fri, 6 Oct 2017 14:45:30 +0000 (17:45 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Oct 2017 08:39:11 +0000 (10:39 +0200)
commit b3207c65dfafae27e7c492cb9188c0dc0eeaf3fd upstream.

xhci_stop_device() calls xhci_queue_stop_endpoint() multiple times
without checking the return value. xhci_queue_stop_endpoint() can
return error if the HC is already halted or unable to queue commands.
This can cause a deadlock condition as xhci_stop_device() would
end up waiting indefinitely for a completion for the command that
didn't get queued. Fix this by checking the return value and bailing
out of xhci_stop_device() in case of error. This patch happens to fix
potential memory leaks of the allocated command structures as well.

Fixes: c311e391a7ef ("xhci: rework command timeout and cancellation,")
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-hub.c

index 950dee33bfccaea5adb349692a7e5ef209a4b803..ad4c697cf30e643e960ece31ed2a35acaf0bdb70 100644 (file)
@@ -420,14 +420,25 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
                                                     GFP_NOWAIT);
                        if (!command) {
                                spin_unlock_irqrestore(&xhci->lock, flags);
-                               xhci_free_command(xhci, cmd);
-                               return -ENOMEM;
+                               ret = -ENOMEM;
+                               goto cmd_cleanup;
+                       }
+
+                       ret = xhci_queue_stop_endpoint(xhci, command, slot_id,
+                                                      i, suspend);
+                       if (ret) {
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               xhci_free_command(xhci, command);
+                               goto cmd_cleanup;
                        }
-                       xhci_queue_stop_endpoint(xhci, command, slot_id, i,
-                                                suspend);
                }
        }
-       xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
+       ret = xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
+       if (ret) {
+               spin_unlock_irqrestore(&xhci->lock, flags);
+               goto cmd_cleanup;
+       }
+
        xhci_ring_cmd_db(xhci);
        spin_unlock_irqrestore(&xhci->lock, flags);
 
@@ -439,6 +450,8 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
                xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
                ret = -ETIME;
        }
+
+cmd_cleanup:
        xhci_free_command(xhci, cmd);
        return ret;
 }