core: really fix dangling devices
authorNathan Hjelm <hjelmn@google.com>
Fri, 11 Jun 2021 13:59:23 +0000 (07:59 -0600)
committerNathan Hjelm <hjelmn@google.com>
Fri, 11 Jun 2021 13:59:23 +0000 (07:59 -0600)
The prior fix made an incorrect assumption about the reference counting of
parent devices. This CL fixes the issue by checking if the parent device
should be removed from the list as well. This is needed as when the child
device is released the parent may also be released.

Closes #924

Signed-off-by: Nathan Hjelm <hjelmn@google.com>
libusb/hotplug.c
libusb/version_nano.h

index b6f896a..52ce626 100644 (file)
@@ -166,7 +166,6 @@ void usbi_hotplug_exit(struct libusb_context *ctx)
        struct usbi_hotplug_callback *hotplug_cb, *next_cb;
        struct usbi_hotplug_message *msg;
        struct libusb_device *dev, *next_dev;
-       int devices_released;
 
        /* check for hotplug support */
        if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
@@ -192,19 +191,22 @@ void usbi_hotplug_exit(struct libusb_context *ctx)
        }
 
        /* free all discovered devices. due to parent references loop until no devices are freed. */
-       do {
-               devices_released = 0;
-               for_each_device_safe(ctx, dev, next_dev) {
-                       /* remove the device from the usb_devs list only if there are no
-                        * references held, otherwise leave it on the list so that a
-                        * warning message will be shown */
-                       if (usbi_atomic_load(&dev->refcnt) == 1) {
-                               ++devices_released;
-                               list_del(&dev->list);
-                       }
-                       libusb_unref_device(dev);
+       for_each_device_safe(ctx, dev, next_dev) {
+               /* remove the device from the usb_devs list only if there are no
+                * references held, otherwise leave it on the list so that a
+                * warning message will be shown */
+               if (usbi_atomic_load(&dev->refcnt) == 1) {
+                       list_del(&dev->list);
+               }
+               if (dev->parent_dev && usbi_atomic_load(&dev->parent_dev->refcnt) == 1) {
+                       /* the parent was before this device in the list and will be released.
+                          remove it from the list. this is safe as parent_dev can not be
+                          equal to next_dev. */
+                       assert (dev->parent_dev != next_dev);
+                       list_del(&dev->parent_dev->list);
                }
-       } while (devices_released > 0);
+               libusb_unref_device(dev);
+       }
 
        usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
 }
index 043064c..b5bcf19 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11627
+#define LIBUSB_NANO 11628