From 5bf9ffaea9df94a9245369f20b1faaaa22355933 Mon Sep 17 00:00:00 2001 From: "seer-by-sentry[bot]" <157164994+seer-by-sentry[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:00:22 +0000 Subject: [PATCH] HTTP: Prevent callbacks during HTTPManager shutdown to avoid use-after-free --- .../Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h | 6 ++++-- .../Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp | 3 ++- .../Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h index 66ee38bc19c..4ce8ad8eb57 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.h @@ -51,7 +51,7 @@ class HTTPRequest bool InvokeDelayAction(); bool WaitingDelayAction() const { return m_timeRequestComplete != -1; } #endif - void Threaded_SetComplete(CURLcode result); + void Threaded_SetComplete(CURLcode result, bool bSkipCallback = false); // mainly used for downloads std::vector GetBuffer() { return m_vecBuffer; } @@ -91,10 +91,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..a574a8e115f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPManager.cpp @@ -91,7 +91,8 @@ void HTTPManager::Shutdown() HTTPRequest* pRequest = *it; if (pRequest != nullptr && pRequest->EasyHandleMatches(pCurlHandle)) { - pRequest->Threaded_SetComplete(m->data.result); + // Skip callbacks during shutdown to avoid use-after-free + pRequest->Threaded_SetComplete(m->data.result, true); delete pRequest; m_vecRequestsInFlight.erase(it); break; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp index ac07945109e..235af6a0231 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/HTTP/HTTPRequest.cpp @@ -90,7 +90,7 @@ void HTTPRequest::OnResponsePartialWrite(std::uint8_t* pBuffer, size_t numBytes) void HTTPRequest::InvokeCallbackIfComplete() { - if (m_bIsComplete) + if (m_bIsComplete && !m_bSkipCallback) { if (m_completionCallback != nullptr) { @@ -133,12 +133,13 @@ bool HTTPRequest::InvokeDelayAction() #endif -void HTTPRequest::Threaded_SetComplete(CURLcode result) +void HTTPRequest::Threaded_SetComplete(CURLcode result, bool bSkipCallback) { // store response code curl_easy_getinfo(m_pCURL, CURLINFO_RESPONSE_CODE, &m_responseCode); m_bIsComplete = true; + m_bSkipCallback = bSkipCallback; // finalize the size, so we can use .size etc m_vecBuffer.resize(m_currentBufSize_Used);