From e57812f610084b766f52d9fd730aacf906985eec Mon Sep 17 00:00:00 2001 From: "seer-by-sentry[bot]" <157164994+seer-by-sentry[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 01:45:11 +0000 Subject: [PATCH] OnlineServices: Ensure HTTPManager is deleted before interface objects --- .../GeneralsOnline/OnlineServices_Init.h | 15 +++++++++------ .../GeneralsOnline/HTTP/HTTPRequest.cpp | 6 ++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h index e141cecb93c..406222fce3d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_Init.h @@ -398,6 +398,15 @@ class NGMP_OnlineServicesManager ~NGMP_OnlineServicesManager() { + // IMPORTANT: Delete HTTPManager BEFORE deleting interface objects + // HTTPManager::Shutdown() will invoke completion callbacks, which may reference interface objects + // We must ensure interfaces are still valid when callbacks execute + if (m_pHTTPManager != nullptr) + { + delete m_pHTTPManager; + m_pHTTPManager = nullptr; + } + if (m_pAuthInterface != nullptr) { delete m_pAuthInterface; @@ -422,12 +431,6 @@ class NGMP_OnlineServicesManager m_pRoomInterface = nullptr; } - if (m_pHTTPManager != nullptr) - { - delete m_pHTTPManager; - m_pHTTPManager = nullptr; - } - // Reset shared_ptr, which will delete WebSocket only when all references are released m_pWebSocket.reset(); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp index ac07945109e..91a2efb731c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp @@ -104,7 +104,13 @@ void HTTPRequest::InvokeCallbackIfComplete() { strResponse.clear(); } + + // Invoke the callback m_completionCallback(true, m_responseCode, strResponse, this); + + // Clear the callback immediately after invocation to prevent use-after-free + // during std::function cleanup, especially during shutdown scenarios + m_completionCallback = nullptr; } } }