From 65fd63369411240dd54cad74916c6a6739e9363b Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Thu, 30 Jan 2020 18:03:34 -0500 Subject: [PATCH] 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. --- src/joystick/darwin/SDL_sysjoystick.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index 8ea9b510e110a..3900440980b92 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -128,6 +128,7 @@ FreeDevice(recDevice *removeDevice) if (removeDevice) { if (removeDevice->deviceRef) { IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE); + CFRelease(removeDevice->deviceRef); removeDevice->deviceRef = NULL; } @@ -206,7 +207,11 @@ JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender) { recDevice *device = (recDevice *) ctx; 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) { FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref); device->ffeffect_ref = NULL; @@ -428,6 +433,15 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) 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; refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey)); @@ -546,12 +560,12 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic } if (!GetDeviceInfo(ioHIDDeviceObject, device)) { - SDL_free(device); + FreeDevice(device); return; /* not a device we care about, probably. */ } if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) { - SDL_free(device); + FreeDevice(device); return; }