From 016b479bbf2b8a487445455ad29b0dcf5f8e32cd Mon Sep 17 00:00:00 2001 From: "seer-by-sentry[bot]" <157164994+seer-by-sentry[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:48:33 +0000 Subject: [PATCH] Fix: Clear stale drawable pointer to prevent use-after-free --- .../Source/GameClient/GUI/ControlBar/ControlBar.cpp | 10 ++++++++++ .../Source/GameClient/GUI/ControlBar/ControlBar.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index 1690c0c91be..85ae8ab2378 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1609,6 +1609,16 @@ void ControlBar::onDrawableDeselected( Drawable *draw ) // set a dirty flag so next time we update we can reconstruct the UI markUIDirty(); + // TheSuperHackers @fix Clear the stale drawable pointer to prevent use-after-free crashes. + // m_currentSelectedDrawable is set in switchToContext but never cleared on drawable destruction. + // When destroyDrawable is called, disregardDrawable -> deselectDrawable -> onDrawableDeselected + // fires before deleteInstance(draw), so this check is safe and prevents the dangling pointer + // from being dereferenced in subsequent update/evaluateContextUI calls. + if( m_currentSelectedDrawable == draw ) + { + m_currentSelectedDrawable = nullptr; + } + if (TheInGameUI->getSelectCount() == 0) { // we just deselected everything - cancel any pending GUI commands diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp index f6ffdb6d086..ea610ea053f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp @@ -1634,6 +1634,16 @@ void ControlBar::onDrawableDeselected( Drawable *draw ) // set a dirty flag so next time we update we can reconstruct the UI markUIDirty(); + // TheSuperHackers @fix Clear the stale drawable pointer to prevent use-after-free crashes. + // m_currentSelectedDrawable is set in switchToContext but never cleared on drawable destruction. + // When destroyDrawable is called, disregardDrawable -> deselectDrawable -> onDrawableDeselected + // fires before deleteInstance(draw), so this check is safe and prevents the dangling pointer + // from being dereferenced in subsequent update/evaluateContextUI calls. + if( m_currentSelectedDrawable == draw ) + { + m_currentSelectedDrawable = nullptr; + } + if (TheInGameUI->getSelectCount() == 0) { // we just deselected everything - cancel any pending GUI commands