Skip to content

Commit 952b2ce

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 952b2ce

File tree

2 files changed

+93
-13
lines changed

2 files changed

+93
-13
lines changed

libusb/os/darwin_usb.c

Lines changed: 89 additions & 11 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,8 @@ 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);
462+
usbi_mutex_destroy (&cached_dev->open_mutex);
460463
free (cached_dev);
461464
}
462465
}
@@ -1358,6 +1361,10 @@ static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io
13581361
(*device)->GetLocationID (device, &new_device->location);
13591362
new_device->port = port;
13601363
new_device->parent_session = parent_sessionID;
1364+
1365+
/* initialize locks */
1366+
usbi_mutex_init (&new_device->capture_mutex);
1367+
usbi_mutex_init (&new_device->open_mutex);
13611368
} else {
13621369
/* release the ref to old device's service */
13631370
IOObjectRelease (new_device->service);
@@ -1519,7 +1526,8 @@ static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) {
15191526
return LIBUSB_SUCCESS;
15201527
}
15211528

1522-
static int darwin_open (struct libusb_device_handle *dev_handle) {
1529+
/* call holding open_mutex lock */
1530+
static int _darwin_open_locked (struct libusb_device_handle *dev_handle) {
15231531
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
15241532
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
15251533
IOReturn kresult;
@@ -1569,7 +1577,18 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
15691577
return 0;
15701578
}
15711579

1572-
static void darwin_close (struct libusb_device_handle *dev_handle) {
1580+
static int darwin_open (struct libusb_device_handle *dev_handle) {
1581+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
1582+
enum libusb_error ret;
1583+
1584+
usbi_mutex_lock (&dpriv->open_mutex);
1585+
ret = _darwin_open_locked (dev_handle);
1586+
usbi_mutex_unlock (&dpriv->open_mutex);
1587+
return ret;
1588+
}
1589+
1590+
/* call holding open_mutex lock */
1591+
static void _darwin_close_locked (struct libusb_device_handle *dev_handle) {
15731592
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
15741593
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
15751594
IOReturn kresult;
@@ -1613,6 +1632,14 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
16131632
}
16141633
}
16151634

1635+
static void darwin_close (struct libusb_device_handle *dev_handle) {
1636+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
1637+
1638+
usbi_mutex_lock (&dpriv->open_mutex);
1639+
_darwin_close_locked (dev_handle);
1640+
usbi_mutex_unlock (&dpriv->open_mutex);
1641+
}
1642+
16161643
static int darwin_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) {
16171644
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
16181645

@@ -2011,11 +2038,15 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, uint8_
20112038
unsigned long claimed_interfaces) {
20122039
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
20132040
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
2014-
int open_count = dpriv->open_count;
2041+
int open_count;
20152042
int ret;
20162043

20172044
struct libusb_context *ctx = HANDLE_CTX (dev_handle);
20182045

2046+
usbi_mutex_lock (&dpriv->open_mutex);
2047+
2048+
open_count = dpriv->open_count;
2049+
20192050
/* clear claimed interfaces temporarily */
20202051
dev_handle->claimed_interfaces = 0;
20212052

@@ -2024,11 +2055,14 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, uint8_
20242055
dpriv->open_count = 1;
20252056

20262057
/* clean up open interfaces */
2027-
(void) darwin_close (dev_handle);
2058+
(void) _darwin_close_locked (dev_handle);
20282059

20292060
/* re-open the device */
2030-
ret = darwin_open (dev_handle);
2061+
ret = _darwin_open_locked (dev_handle);
20312062
dpriv->open_count = open_count;
2063+
2064+
usbi_mutex_unlock (&dpriv->open_mutex);
2065+
20322066
if (LIBUSB_SUCCESS != ret) {
20332067
/* could not restore configuration */
20342068
return LIBUSB_ERROR_NOT_FOUND;
@@ -2069,6 +2103,7 @@ static int darwin_restore_state (struct libusb_device_handle *dev_handle, uint8_
20692103
return LIBUSB_SUCCESS;
20702104
}
20712105

2106+
/* call holding capture_mutex lock */
20722107
static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, bool capture) {
20732108
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
20742109
unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
@@ -2168,7 +2203,8 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b
21682203
return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
21692204
}
21702205

2171-
static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
2206+
/* call holding capture_mutex lock */
2207+
static int _darwin_reset_device_locked (struct libusb_device_handle *dev_handle) {
21722208
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
21732209
IOReturn kresult;
21742210
enum libusb_error ret;
@@ -2194,7 +2230,7 @@ static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
21942230
/* reset capture count */
21952231
dpriv->capture_count = 0;
21962232
/* attempt to detach kernel driver again as it is now re-attached */
2197-
ret = darwin_detach_kernel_driver (dev_handle, 0);
2233+
ret = _darwin_detach_kernel_driver_locked (dev_handle, 0);
21982234
if (ret != LIBUSB_SUCCESS) {
21992235
return ret;
22002236
}
@@ -2207,6 +2243,16 @@ static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
22072243
return ret;
22082244
}
22092245

2246+
static int darwin_reset_device (struct libusb_device_handle *dev_handle) {
2247+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2248+
enum libusb_error ret;
2249+
2250+
usbi_mutex_lock (&dpriv->capture_mutex);
2251+
ret = _darwin_reset_device_locked (dev_handle);
2252+
usbi_mutex_unlock (&dpriv->capture_mutex);
2253+
return ret;
2254+
}
2255+
22102256
static io_service_t usb_find_interface_matching_location (const io_name_t class_name, UInt8 interface_number, UInt32 location) {
22112257
CFMutableDictionaryRef matchingDict = IOServiceMatching (class_name);
22122258
CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
@@ -2822,7 +2868,8 @@ static int darwin_reload_device (struct libusb_device_handle *dev_handle) {
28222868

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

2825-
static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2871+
/* call holding capture_mutex lock */
2872+
static int _darwin_detach_kernel_driver_locked (struct libusb_device_handle *dev_handle, uint8_t interface) {
28262873
UNUSED(interface);
28272874
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
28282875
IOReturn kresult;
@@ -2867,8 +2914,18 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle,
28672914
return LIBUSB_SUCCESS;
28682915
}
28692916

2917+
static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2918+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2919+
enum libusb_error ret;
2920+
2921+
usbi_mutex_lock (&dpriv->capture_mutex);
2922+
ret = _darwin_detach_kernel_driver_locked (dev_handle, interface);
2923+
usbi_mutex_unlock (&dpriv->capture_mutex);
2924+
return ret;
2925+
}
28702926

2871-
static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2927+
/* call holding capture_mutex lock */
2928+
static int _darwin_attach_kernel_driver_locked (struct libusb_device_handle *dev_handle, uint8_t interface) {
28722929
UNUSED(interface);
28732930
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
28742931

@@ -2887,6 +2944,16 @@ static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle,
28872944
return darwin_reenumerate_device (dev_handle, false);
28882945
}
28892946

2947+
static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, uint8_t interface) {
2948+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2949+
enum libusb_error ret;
2950+
2951+
usbi_mutex_lock (&dpriv->capture_mutex);
2952+
ret = _darwin_attach_kernel_driver_locked (dev_handle, interface);
2953+
usbi_mutex_unlock (&dpriv->capture_mutex);
2954+
return ret;
2955+
}
2956+
28902957
static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
28912958
enum libusb_error ret;
28922959
if (dev_handle->auto_detach_kernel_driver && darwin_kernel_driver_active(dev_handle, iface)) {
@@ -2899,7 +2966,8 @@ static int darwin_capture_claim_interface(struct libusb_device_handle *dev_handl
28992966
return darwin_claim_interface (dev_handle, iface);
29002967
}
29012968

2902-
static int darwin_capture_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
2969+
/* call holding capture_mutex lock */
2970+
static int _darwin_capture_release_interface_locked(struct libusb_device_handle *dev_handle, uint8_t iface) {
29032971
enum libusb_error ret;
29042972
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
29052973

@@ -2909,7 +2977,7 @@ static int darwin_capture_release_interface(struct libusb_device_handle *dev_han
29092977
}
29102978

29112979
if (dev_handle->auto_detach_kernel_driver && dpriv->capture_count > 0) {
2912-
ret = darwin_attach_kernel_driver (dev_handle, iface);
2980+
ret = _darwin_attach_kernel_driver_locked (dev_handle, iface);
29132981
if (LIBUSB_SUCCESS != ret) {
29142982
usbi_info (HANDLE_CTX (dev_handle), "on attempt to reattach the kernel driver got ret=%d", ret);
29152983
}
@@ -2919,6 +2987,16 @@ static int darwin_capture_release_interface(struct libusb_device_handle *dev_han
29192987
return LIBUSB_SUCCESS;
29202988
}
29212989

2990+
static int darwin_capture_release_interface (struct libusb_device_handle *dev_handle, uint8_t iface) {
2991+
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
2992+
enum libusb_error ret;
2993+
2994+
usbi_mutex_lock (&dpriv->capture_mutex);
2995+
ret = _darwin_capture_release_interface_locked (dev_handle, iface);
2996+
usbi_mutex_unlock (&dpriv->capture_mutex);
2997+
return ret;
2998+
}
2999+
29223000
#endif
29233001

29243002
const struct usbi_os_backend usbi_backend = {

libusb/os/darwin_usb.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,14 @@ struct darwin_cached_device {
113113
char sys_path[21];
114114
usb_device_t device;
115115
io_service_t service;
116-
int open_count;
116+
usbi_mutex_t open_mutex;
117+
int open_count; // GUARDED_BY(open_mutex)
117118
UInt8 first_config, active_config, port;
118119
int can_enumerate;
119120
int refcount;
120121
bool in_reenumerate;
121-
int capture_count;
122+
usbi_mutex_t capture_mutex;
123+
int capture_count; // GUARDED_BY(capture_mutex)
122124
};
123125

124126
struct darwin_device_priv {

0 commit comments

Comments
 (0)