Skip to content

Commit 07fa7e8

Browse files
CopilotYouw
andcommitted
Fix race condition in libusb hotplug: protect devs with mutex in process_hotplug_event
In process_hotplug_event(), the devs list was modified without holding hid_hotplug_context.mutex. Meanwhile, hid_hotplug_register_callback() iterates devs under that mutex during HID_API_HOTPLUG_ENUMERATE. A device disconnect processed by callback_thread could free a devs entry while the ENUMERATE loop was still traversing it, causing a use-after-free. Fix by wrapping process_hotplug_event() with hid_hotplug_context.mutex. The mutex is recursive, so hid_internal_invoke_callbacks() (which also acquires it) works correctly. This aligns with how the linux backend handles the same scenario. Agent-Logs-Url: https://github.com/libusb/hidapi/sessions/cb1dc652-1f96-457d-8319-55e4a6f6e6f3 Co-authored-by: Youw <5939659+Youw@users.noreply.github.com>
1 parent e43174a commit 07fa7e8

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

libusb/hid.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ static struct hid_hotplug_context {
173173
struct hid_hotplug_callback *hotplug_cbs;
174174

175175
/* Linked list of the device infos (mandatory when the device is disconnected).
176-
* Initialized before callback_thread is created; exclusively owned by
177-
* callback_thread after that (both reads/writes and final cleanup). */
176+
* Protected by `mutex` for both reads and writes.
177+
* Final cleanup is done by callback_thread during shutdown. */
178178
struct hid_device_info *devs;
179179
} hid_hotplug_context = {
180180
.next_handle = FIRST_HOTPLUG_CALLBACK_HANDLE,
@@ -1165,6 +1165,11 @@ static int hid_libusb_hotplug_callback(libusb_context *ctx, libusb_device *devic
11651165

11661166
static void process_hotplug_event(struct hid_hotplug_queue* msg)
11671167
{
1168+
/* Lock the mutex to avoid race conditions with hid_hotplug_register_callback(),
1169+
* which may iterate devs during HID_API_HOTPLUG_ENUMERATE while holding this mutex.
1170+
* The mutex is recursive, so hid_internal_invoke_callbacks() can safely re-acquire it. */
1171+
pthread_mutex_lock(&hid_hotplug_context.mutex);
1172+
11681173
if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
11691174
struct hid_device_info* info = hid_enumerate_from_libusb(msg->device, 0, 0);
11701175
struct hid_device_info* info_cur = info;
@@ -1205,6 +1210,8 @@ static void process_hotplug_event(struct hid_hotplug_queue* msg)
12051210
}
12061211
}
12071212

1213+
pthread_mutex_unlock(&hid_hotplug_context.mutex);
1214+
12081215
/* Release the libusb device - we are done with it */
12091216
libusb_unref_device(msg->device);
12101217

0 commit comments

Comments
 (0)