Tim Clem : dinput: Lock around polling a HID device in joystick_osx.
Alexandre Julliard
julliard at winehq.org
Thu Sep 30 16:04:41 CDT 2021
Module: wine
Branch: master
Commit: 5853f356b8f0ede9a38f7833cdfe0806b84aa9bd
URL: https://source.winehq.org/git/wine.git/?a=commit;h=5853f356b8f0ede9a38f7833cdfe0806b84aa9bd
Author: Tim Clem <tclem at codeweavers.com>
Date: Wed Sep 29 13:01:54 2021 -0700
dinput: Lock around polling a HID device in joystick_osx.
If multiple threads try to read IOHIDElements and IOHIDValues from
the same IOHIDDevice simultaneously, we sometimes crash deep in
IOKit.
Fixes a crash in GTA 4 when using a PS4 controller.
Signed-off-by: Tim Clem <tclem at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
dlls/dinput/joystick_osx.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/joystick_osx.c b/dlls/dinput/joystick_osx.c
index 2db9e521a1e..9ee45e8a445 100644
--- a/dlls/dinput/joystick_osx.c
+++ b/dlls/dinput/joystick_osx.c
@@ -100,6 +100,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput);
static CFMutableArrayRef device_main_elements = NULL;
+/* Maps IOHIDDeviceRefs to CRITICAL_SECTION pointers. */
+static CFMutableDictionaryRef hid_device_crits = NULL;
+
typedef struct JoystickImpl JoystickImpl;
static const IDirectInputDevice8WVtbl JoystickWvt;
@@ -534,8 +537,11 @@ static int find_osx_devices(void)
CFArraySortValues(devices, CFRangeMake(0, num_devices), device_location_name_comparator, NULL);
device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- if (!device_main_elements)
+ hid_device_crits = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
+ if (!device_main_elements || !hid_device_crits)
{
+ if (device_main_elements) CFRelease(device_main_elements);
+ if (hid_device_crits) CFRelease(hid_device_crits);
CFRelease( devices );
goto fail;
}
@@ -550,6 +556,15 @@ static int find_osx_devices(void)
TRACE("hid_device %s\n", debugstr_device(hid_device));
top = find_top_level(hid_device, device_main_elements);
num_main_elements += top;
+
+ if ( top ) {
+ /* This HID device has relevent elements, so it needs a critical section. */
+ CRITICAL_SECTION *cs = HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(cs);
+ cs->DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_device_crits");
+
+ CFDictionaryAddValue(hid_device_crits, hid_device, cs);
+ }
}
CFRelease(devices);
@@ -797,6 +812,20 @@ static IOReturn get_element_values(IOHIDDeviceRef hid_device, JoystickImpl *devi
IOHIDElementRef element;
IOHIDValueRef valueRef;
+ /* If more than one thread is trying to poll the same HID device, we can
+ * crash deep inside IOKit. So we enter a per-IOHIDDevice critical section
+ * here. Note that it must be per-HID device and not just per-JoystickImpl,
+ * as there may be multiple JoystickImpls referencing the same underlying
+ * device. */
+
+ CRITICAL_SECTION *crit = (CRITICAL_SECTION *)CFDictionaryGetValue(hid_device_crits, hid_device);
+ if (!crit) {
+ ERR("missing critical section for device %s\n", debugstr_device(hid_device));
+ return kIOReturnError;
+ }
+
+ EnterCriticalSection(crit);
+
for (i = 0; i < element_count; i++)
{
element = (IOHIDElementRef)CFArrayGetValueAtIndex(device->elements, i);
@@ -810,6 +839,7 @@ static IOReturn get_element_values(IOHIDDeviceRef hid_device, JoystickImpl *devi
device->element_values[i] = IOHIDValueGetIntegerValue(valueRef);
}
+ LeaveCriticalSection(crit);
return ret;
}
More information about the wine-cvs
mailing list