From 86c921edcc2fbf4da5f5c5ef9b124216a4c8af4e Mon Sep 17 00:00:00 2001 From: "seer-by-sentry[bot]" <157164994+seer-by-sentry[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:06:43 +0000 Subject: [PATCH] HTTP: Prevent use-after-free in HTTPRequest callback during shutdown --- .../Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h | 7 ++++++- .../Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp | 2 ++ .../Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h index 66ee38bc19c..63316444a38 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h @@ -59,6 +59,9 @@ class HTTPRequest std::string GetURI() { return m_strURI; } + void SetSkipCallback(bool bSkip) { m_bSkipCallback = bSkip; } + + private: void PlatformStartRequest(); @@ -91,10 +94,12 @@ class HTTPRequest bool m_bNeedsProgressUpdate = false; bool m_bIsStarted = false; bool m_bIsComplete = false; + bool m_bSkipCallback = false; + struct curl_slist* headers = nullptr; std::function m_completionCallback = nullptr; std::function m_progressCallback = nullptr; -}; +}; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp index 8ac79982f8b..ae64f3eb97c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp @@ -91,6 +91,8 @@ void HTTPManager::Shutdown() HTTPRequest* pRequest = *it; if (pRequest != nullptr && pRequest->EasyHandleMatches(pCurlHandle)) { + // Skip callback invocation during shutdown to prevent use-after-free + pRequest->SetSkipCallback(true); pRequest->Threaded_SetComplete(m->data.result); delete pRequest; m_vecRequestsInFlight.erase(it); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp index ac07945109e..6bcfcf19b58 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp @@ -92,6 +92,12 @@ void HTTPRequest::InvokeCallbackIfComplete() { if (m_bIsComplete) { + // Skip callback invocation if we're shutting down to avoid use-after-free + if (m_bSkipCallback) + { + return; + } + if (m_completionCallback != nullptr) { // Convert m_vecBuffer to std::string for m_strResponse