FIX for SDL-4927: CFRetain+CFRelease a game controller's IOKit object
authorDavid Ludwig <dludwig@pobox.com>
Thu, 30 Jan 2020 18:03:34 -0500
changeset 134839c763de27c67
parent 13482 19e0bdbce46d
child 13484 b17ae2c3cd9b
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
     1.1 --- a/src/joystick/darwin/SDL_sysjoystick.c	Tue Feb 04 15:27:25 2020 -0800
     1.2 +++ b/src/joystick/darwin/SDL_sysjoystick.c	Thu Jan 30 18:03:34 2020 -0500
     1.3 @@ -128,6 +128,7 @@
     1.4      if (removeDevice) {
     1.5          if (removeDevice->deviceRef) {
     1.6              IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
     1.7 +            CFRelease(removeDevice->deviceRef);
     1.8              removeDevice->deviceRef = NULL;
     1.9          }
    1.10  
    1.11 @@ -206,7 +207,11 @@
    1.12  {
    1.13      recDevice *device = (recDevice *) ctx;
    1.14      device->removed = SDL_TRUE;
    1.15 -    device->deviceRef = NULL; // deviceRef was invalidated due to the remove
    1.16 +    if (device->deviceRef) {
    1.17 +        // deviceRef was invalidated due to the remove
    1.18 +        CFRelease(device->deviceRef);
    1.19 +        device->deviceRef = NULL;
    1.20 +    }
    1.21      if (device->ffeffect_ref) {
    1.22          FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
    1.23          device->ffeffect_ref = NULL;
    1.24 @@ -428,6 +433,15 @@
    1.25          return SDL_FALSE; /* Filter device list to non-keyboard/mouse stuff */
    1.26      }
    1.27  
    1.28 +    /* Make sure we retain the use of the IOKit-provided device-object,
    1.29 +       lest the device get disconnected and we try to use it.  (Fixes
    1.30 +       SDL-Bugzilla #4961, aka. https://bugzilla.libsdl.org/show_bug.cgi?id=4961 )
    1.31 +    */
    1.32 +    CFRetain(hidDevice);
    1.33 +
    1.34 +    /* Now that we've CFRetain'ed the device-object (for our use), we'll
    1.35 +       save the reference to it.
    1.36 +    */
    1.37      pDevice->deviceRef = hidDevice;
    1.38  
    1.39      refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
    1.40 @@ -546,12 +560,12 @@
    1.41      }
    1.42  
    1.43      if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
    1.44 -        SDL_free(device);
    1.45 +        FreeDevice(device);
    1.46          return;   /* not a device we care about, probably. */
    1.47      }
    1.48  
    1.49      if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
    1.50 -        SDL_free(device);
    1.51 +        FreeDevice(device);
    1.52          return;
    1.53      }
    1.54