summary refs log tree commit diff
path: root/devices/src/usb
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2019-05-03 09:40:48 -0700
committerDaniel Verkamp <dverkamp@chromium.org>2019-05-03 18:49:09 +0000
commitf2346c1b5f30bb3ddf72ade48034f72ea48d2d6e (patch)
treefa8bc316ae2e11739389eb68996f2f4e72c17e30 /devices/src/usb
parent5dd8694d77f001f581a3f839621df55f357ca1eb (diff)
downloadcrosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar.gz
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar.bz2
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar.lz
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar.xz
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.tar.zst
crosvm-f2346c1b5f30bb3ddf72ade48034f72ea48d2d6e.zip
usb: fix deadlock in try_detach removal path
The UsbHub::try_detach function, which is only called from the libusb
hotplug callback when devices are removed, calls into
port.get_backend_device(), which acquires the backend_device lock, and
then calls port.detach(), which also tries to acquire the backend_device
lock, while still holding onto the backend_device. This clearly leads to
a deadlock.

Add an extra block in try_detach to keep the backend_device lock scoped
so that it is dropped before calling port.detach().

BUG=chromium:958117
TEST=Hot remove usb device from Crostini; verify it is gone in lsusb

Change-Id: Ibbbf68623390e3d25576f544b815c2a5607934fc
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1594158
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/usb')
-rw-r--r--devices/src/usb/xhci/usb_hub.rs30
1 files changed, 19 insertions, 11 deletions
diff --git a/devices/src/usb/xhci/usb_hub.rs b/devices/src/usb/xhci/usb_hub.rs
index ae4f2f8..28c7f19 100644
--- a/devices/src/usb/xhci/usb_hub.rs
+++ b/devices/src/usb/xhci/usb_hub.rs
@@ -208,20 +208,28 @@ impl UsbHub {
     /// Try to detach device of bus, addr, vid, pid
     pub fn try_detach(&self, bus: u8, addr: u8, vid: u16, pid: u16) -> Result<()> {
         for port in &self.ports {
-            let backend_device = port.get_backend_device();
+            // This block exists so that we only hold the backend device
+            // lock while checking the address. It needs to be dropped before
+            // calling port.detach(), because that acquires the backend
+            // device lock again.
+            {
+                let backend_device = port.get_backend_device();
 
-            let d = match backend_device.as_ref() {
-                None => continue,
-                Some(d) => d,
-            };
+                let d = match backend_device.as_ref() {
+                    None => continue,
+                    Some(d) => d,
+                };
 
-            if d.host_bus() == bus
-                && d.host_address() == addr
-                && d.get_vid() == vid
-                && d.get_pid() == pid
-            {
-                return port.detach();
+                if d.host_bus() != bus
+                    || d.host_address() != addr
+                    || d.get_vid() != vid
+                    || d.get_pid() != pid
+                {
+                    continue;
+                }
             }
+
+            return port.detach();
         }
 
         Err(Error::NoSuchDevice {