Skip to content

Commit 440cc1b

Browse files
committed
darwin: fix race condition on device capture
If two threads try to capture at the same time, the result can be inconsistent. For example, a process can have two different contexts to libusb and both call `libusb_reset_device`.
1 parent 7313527 commit 440cc1b

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

libusb/os/darwin_usb.c

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static int darwin_reenumerate_device(struct libusb_device_handle *dev_handle, bo
9090
static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
9191
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
9292
static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface);
93+
static int _darwin_detach_kernel_driver_locked (struct libusb_device_handle *dev_handle, uint8_t interface);
9394
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
9495

9596
static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
@@ -457,6 +458,7 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev)
457458
cached_dev->device = NULL;
458459
}
459460
IOObjectRelease (cached_dev->service);
461+
usbi_mutex_destroy (&cached_dev->capture_mutex);
460462
free (cached_dev);
461463
}
462464
}
@@ -1358,6 +1360,9 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io
13581360
(*device)->GetLocationID (device, &new_device->location);
13591361
new_device->port = port;
13601362
new_device->parent_session = parent_sessionID;
1363+
1364+
/* initialize locks */
1365+
usbi_mutex_init (&new_device->capture_mutex);
13611366
} else {
13621367
/* release the ref to old device's service */
13631368
IOObjectRelease (new_device->service);
@@ -2069,6 +2074,7 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, uint8_
20692074
return LIBUSB_SUCCESS;
20702075
}
20712076

2077+
/* call holding capture_mutex lock */
20722078
static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) {
20732079
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
20742080
unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
@@ -2168,7 +2174,8 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b
21682174
return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
21692175
}
21702176

2171-
static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
2177+
/* call holding capture_mutex lock */
2178+
static int _darwin_reset_device_locked (struct libusb_device_handle *dev_handle) {
21722179
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
21732180
IOReturn kresult;
21742181
enum libusb_error ret;
@@ -2194,7 +2201,7 @@ static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
21942201
/* reset capture count */
21952202
dpriv->capture_count = 0;
21962203
/* attempt to detach kernel driver again as it is now re-attached */
2197-
ret = darwin_detach_kernel_driver (dev_handle, 0);
2204+
ret = _darwin_detach_kernel_driver_locked (dev_handle, 0);
21982205
if (ret != LIBUSB_SUCCESS) {
21992206
return ret;
22002207
}
@@ -2207,6 +2214,16 @@ static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
22072214
return ret;
22082215
}
22092216

2217+
static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
2218+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2219+
enum libusb_error ret;
2220+
2221+
usbi_mutex_lock (&dpriv->capture_mutex);
2222+
ret = _darwin_reset_device_locked (dev_handle);
2223+
usbi_mutex_unlock (&dpriv->capture_mutex);
2224+
return ret;
2225+
}
2226+
22102227
static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) {
22112228
CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name);
22122229
CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
@@ -2822,7 +2839,8 @@ static int darwin_reload_device (struct libusb_device_handle *dev_handle) {
28222839

28232840
/* On macOS, we capture an entire device at once, not individual interfaces. */
28242841

2825-
static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2842+
/* call holding capture_mutex lock */
2843+
static int _darwin_detach_kernel_driver_locked (struct libusb_device_handle *dev_handle, uint8_t interface) {
28262844
UNUSED(interface);
28272845
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
28282846
IOReturn kresult;
@@ -2867,8 +2885,18 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle,
28672885
return LIBUSB_SUCCESS;
28682886
}
28692887

2888+
static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2889+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2890+
enum libusb_error ret;
28702891

2871-
static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2892+
usbi_mutex_lock (&dpriv->capture_mutex);
2893+
ret = _darwin_detach_kernel_driver_locked (dev_handle, interface);
2894+
usbi_mutex_unlock (&dpriv->capture_mutex);
2895+
return ret;
2896+
}
2897+
2898+
/* call holding capture_mutex lock */
2899+
static int _darwin_attach_kernel_driver_locked (struct libusb_device_handle *dev_handle, uint8_t interface) {
28722900
UNUSED(interface);
28732901
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
28742902

@@ -2887,6 +2915,16 @@ static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle,
28872915
return darwin_reenumerate_device (dev_handle, false);
28882916
}
28892917

2918+
static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2919+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2920+
enum libusb_error ret;
2921+
2922+
usbi_mutex_lock (&dpriv->capture_mutex);
2923+
ret = _darwin_attach_kernel_driver_locked (dev_handle, interface);
2924+
usbi_mutex_unlock (&dpriv->capture_mutex);
2925+
return ret;
2926+
}
2927+
28902928
static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
28912929
enum libusb_error ret;
28922930
if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) {
@@ -2899,7 +2937,8 @@ static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handl
28992937
return darwin_claim_interface (dev_handle, iface);
29002938
}
29012939

2902-
static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
2940+
/* call holding capture_mutex lock */
2941+
static int _darwin_capture_release_interface_locked(struct libusb_device_handle *dev_handle, uint8_t iface) {
29032942
enum libusb_error ret;
29042943
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
29052944

@@ -2909,7 +2948,7 @@ static int darwin_capture_release_interface(struct libusb_device_handle *dev_han
29092948
}
29102949

29112950
if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) {
2912-
ret = darwin_attach_kernel_driver (dev_handle, iface);
2951+
ret = _darwin_attach_kernel_driver_locked (dev_handle, iface);
29132952
if (LIBUSB_SUCCESS != ret) {
29142953
usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret);
29152954
}
@@ -2919,6 +2958,16 @@ static int darwin_capture_release_interface(struct libusb_device_handle *dev_han
29192958
return LIBUSB_SUCCESS;
29202959
}
29212960

2961+
static int darwin_capture_release_interface (struct libusb_device_handle *dev_handle, uint8_t iface) {
2962+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2963+
enum libusb_error ret;
2964+
2965+
usbi_mutex_lock (&dpriv->capture_mutex);
2966+
ret = _darwin_capture_release_interface_locked (dev_handle, iface);
2967+
usbi_mutex_unlock (&dpriv->capture_mutex);
2968+
return ret;
2969+
}
2970+
29222971
#endif
29232972

29242973
const struct usbi_os_backend usbi_backend = {

libusb/os/darwin_usb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct darwin_cached_device {
119119
int refcount;
120120
bool in_reenumerate;
121121
int capture_count;
122+
usbi_mutex_t capture_mutex;
122123
};
123124

124125
struct darwin_device_priv {

0 commit comments

Comments
 (0)