SDL: Check for device disconnection in HIDAPI_JoystickOpen()

From 435e7ce663b7e568086c5dc0fb1bb889e41a3ed1 Mon Sep 17 00:00:00 2001
From: Cameron Gutman <[EMAIL REDACTED]>
Date: Tue, 17 Oct 2023 21:41:30 -0500
Subject: [PATCH] Check for device disconnection in HIDAPI_JoystickOpen()

HIDAPI joystick drivers may call HIDAPI_JoystickDisconnected() in their
UpdateDevice() function during HIDAPI_JoystickOpen(). If they do this
today, the opened joystick will end up partially initialized (no name,
path, mapping GUID, etc.) because HIDAPI_GetDeviceByIndex() will no
longer be able to find the SDL_HIDAPI_Device for the removed joystick.

Worse still, joystick->hwdata->device becomes a dangling freed pointer
the next time HIDAPI_UpdateDeviceList() is called. This leads to a UAF
when the application or SDL calls SDL_JoystickClose() on this joystick.

Fix all this by checking if the device no longer has any associated
joysticks after calling UpdateDevice() and failing the open call if so.
---
 src/joystick/hidapi/SDL_hidapijoystick.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 06e6d2607a0c..7fa7da96a7dd 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1440,6 +1440,12 @@ static int HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
     device->updating = SDL_FALSE;
     SDL_UnlockMutex(device->dev_lock);
 
+    /* UpdateDevice() may have called HIDAPI_JoystickDisconnected() if the device went away */
+    if (device->num_joysticks == 0) {
+        SDL_free(hwdata);
+        return SDL_SetError("HIDAPI device disconnected while opening");
+    }
+
     if (!device->driver->OpenJoystick(device, joystick)) {
         /* The open failed, mark this device as disconnected and update devices */
         HIDAPI_JoystickDisconnected(device, joystickID);