FIX for SDL-4927: CFRetain+CFRelease a game controller's IOKit object

This fixes a crash whereby SDL could crash on macOS/Darwin, if and when a
USB game controller gets unplugged.  SDL was not retaining a reference
to the controller's OS/IOKit-provided 'device object', and was capable
of trying to use it, after a device was hot-unplugged.
This commit is contained in:
David Ludwig 2020-01-30 18:03:34 -05:00
parent 02108cf7e5
commit 65fd633694

View file

@ -128,6 +128,7 @@ FreeDevice(recDevice *removeDevice)
if (removeDevice) { if (removeDevice) {
if (removeDevice->deviceRef) { if (removeDevice->deviceRef) {
IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE); IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
CFRelease(removeDevice->deviceRef);
removeDevice->deviceRef = NULL; removeDevice->deviceRef = NULL;
} }
@ -206,7 +207,11 @@ JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
{ {
recDevice *device = (recDevice *) ctx; recDevice *device = (recDevice *) ctx;
device->removed = SDL_TRUE; device->removed = SDL_TRUE;
device->deviceRef = NULL; // deviceRef was invalidated due to the remove if (device->deviceRef) {
// deviceRef was invalidated due to the remove
CFRelease(device->deviceRef);
device->deviceRef = NULL;
}
if (device->ffeffect_ref) { if (device->ffeffect_ref) {
FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref); FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
device->ffeffect_ref = NULL; device->ffeffect_ref = NULL;
@ -428,6 +433,15 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */ return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
} }
/* Make sure we retain the use of the IOKit-provided device-object,
lest the device get disconnected and we try to use it. (Fixes
SDL-Bugzilla #4961, aka. https://bugzilla.libsdl.org/show_bug.cgi?id=4961 )
*/
CFRetain(hidDevice);
/* Now that we've CFRetain'ed the device-object (for our use), we'll
save the reference to it.
*/
pDevice->deviceRef = hidDevice; pDevice->deviceRef = hidDevice;
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey)); refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
@ -546,12 +560,12 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
} }
if (!GetDeviceInfo(ioHIDDeviceObject, device)) { if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
SDL_free(device); FreeDevice(device);
return; /* not a device we care about, probably. */ return; /* not a device we care about, probably. */
} }
if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) { if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
SDL_free(device); FreeDevice(device);
return; return;
} }