core: ensure that all devices are properly cleaned up on libusb_exit
authorNathan Hjelm <hjelmn@google.com>
Fri, 11 Jun 2021 02:51:44 +0000 (20:51 -0600)
committerNathan Hjelm <hjelmn@google.com>
Fri, 11 Jun 2021 02:56:24 +0000 (20:56 -0600)
When cleaning up the context on libusb_exit the last step is to to call
hotplug_exit. This function took one pass over the devices and released any
devices where the reference count had reached 0. All remaining devices
were assumed to have leaked references and a warning message was printed
out. Unfortunately, this cleanup was too simplistic. It ignored the
references created when a device was the parent of another device (which
takes a reference). This reference is released when the child device is
released.

To ensure that we do not erroneously warn about devices with leftover
references the code now loops over the device list until no more devices
have a single reference. This ensures that we will eventually remove
all devices with no external references.

Fixes #924

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

index 387b49f476056c058e29720ae87059abb5046cf7..deb138d8c11d53b7db78e08151061da25faa288e 100644 (file)
@@ -166,6 +166,7 @@ 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))
@@ -190,15 +191,19 @@ void usbi_hotplug_exit(struct libusb_context *ctx)
                free(msg);
        }
 
-       /* free all discovered devices */
-       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);
-               libusb_unref_device(dev);
-       }
+       /* 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)
+                               list_del(&dev->list);
+                       libusb_unref_device(dev);
+                       ++devices_released;
+               }
+       } while (devices_released > 0);
 
        usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
 }
index 9a19b348fa74455f9f2773872682f9290c476f24..21591b92b7b006ee6b25aa949237e30ae9b9d8ed 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11623
+#define LIBUSB_NANO 11624