From 61123dd3c76070acd802daee4b4358d870f9186c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 07:09:11 +0000 Subject: [PATCH] fix(manager): lock updateAll() against hotplug thread to stop disconnect crash GamepadManagerImpl::updateAll() iterated gamepads_ and dereferenced each device every frame without holding mutex_, while the hotplug thread could simultaneously destroy a device (gamepads_[i].reset()) in check_for_disconnected_devices(). That data race / use-after-free crashed the host whenever a controller dropped mid-frame. Take the same timed_mutex every other accessor uses. The hotplug thread already uses try_lock_for and skips a cycle when it can't acquire the lock, so this cannot deadlock game launch. Also dropped the empty dead if-block around updateState(). --- GCPad_Lib/src/GamepadManager.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/GCPad_Lib/src/GamepadManager.cpp b/GCPad_Lib/src/GamepadManager.cpp index 93b3c35..7cebbfa 100644 --- a/GCPad_Lib/src/GamepadManager.cpp +++ b/GCPad_Lib/src/GamepadManager.cpp @@ -276,10 +276,17 @@ void GamepadManagerImpl::setGamepadDisconnectedCallback(GamepadDisconnectedCallb } void GamepadManagerImpl::updateAll() { + // Lock against the hotplug thread. Without this, updateAll() (called every + // frame from the polling thread) iterates gamepads_ and dereferences each + // device while the hotplug thread can simultaneously destroy a device in + // check_for_disconnected_devices() (gamepads_[i].reset()) — a data race and + // use-after-free that crashed the host whenever a pad dropped mid-frame. + // The hotplug thread already uses try_lock_for and simply skips a cycle if + // it can't acquire the lock, so this can never deadlock game launch. + std::lock_guard lock(mutex_); for (auto& gamepad : gamepads_) { if (gamepad) { - if (!gamepad->updateState()) { - } + gamepad->updateState(); } } }