summaryrefslogtreecommitdiff
path: root/libusb/hotplug.c
diff options
context:
space:
mode:
authorNathan Hjelm <hjelmn@google.com>2021-06-11 07:59:23 -0600
committerNathan Hjelm <hjelmn@google.com>2021-06-11 07:59:23 -0600
commit87a8578b9391a4c1a3ed8824d49f6c91cdaea4f2 (patch)
tree3585fc6d912c2a2b730ea7554e2a42b50ab7f680 /libusb/hotplug.c
parent3e674040edde3196c91e6779e32dd8d7eb79ddf6 (diff)
downloadlibusb-87a8578b9391a4c1a3ed8824d49f6c91cdaea4f2.tar.gz
core: really fix dangling devices
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>
Diffstat (limited to 'libusb/hotplug.c')
-rw-r--r--libusb/hotplug.c28
1 files changed, 15 insertions, 13 deletions
diff --git a/libusb/hotplug.c b/libusb/hotplug.c
index b6f896a..52ce626 100644
--- a/libusb/hotplug.c
+++ b/libusb/hotplug.c
@@ -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);
}