Browse Source

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.
David Ludwig 5 years ago
parent
commit
65fd633694
1 changed files with 17 additions and 3 deletions
  1. 17 3
      src/joystick/darwin/SDL_sysjoystick.c

+ 17 - 3
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;
     }