On Windows, getErrorMessage() constructs a std::string from lpMsgBuf without checking whether FormatMessage actually wrote to it. When FormatMessage fails, lpMsgBuf is still uninitialized and std::string's constructor runs strlen on an indeterminate stack value.
It also leaks the LocalAlloc'd buffer on the success path.
Description
src/backend/common/module_loading_windows.cpp:31-41:
string getErrorMessage() {
const char* lpMsgBuf; // uninitialized
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
string error_message(lpMsgBuf); // no check, no LocalFree
return error_message;
}
FormatMessage returns 0 and does not touch the output pointer when the message cannot be formatted — which happens for error codes with no entry in the system message table (common for loader and NTSTATUS-derived codes, and for codes left behind by a DLL whose DllMain failed). The return value is discarded here, so lpMsgBuf is then dereferenced regardless.
FORMAT_MESSAGE_ALLOCATE_BUFFER also requires the caller to LocalFree the buffer; that never happens, so every successful call leaks.
Why this is on a hot path
getErrorMessage() is called on every failed LoadLibrary during backend probing, src/api/unified/symbol_manager.cpp:
AF_TRACE("Failed to load {}", getErrorMessage());
AF_TRACE evaluates its arguments unconditionally — the spdlog level filter applies after the call — so this runs even with tracing disabled. The unified loader tries roughly a dozen path prefixes across each backend, so on a typical process start most of those attempts fail and this function runs tens of times before any user code executes.
Other callers include src/backend/cuda/cusparseModule.cpp, src/backend/cuda/cudnnModule.cpp, and src/backend/common/graphics_common.cpp.
Impact
Reading an indeterminate pointer produces either a garbage error string or STATUS_ACCESS_VIOLATION (0xC0000005), during library initialization and before any user code runs. Intermittent, since it depends on the stack residue at that address.
Reproducible Code and/or Steps
System Information
Checklist
Disclaimer
Found by Claude Opus 5. The prompt was to look for potential sources for 0xC0000005 errors on Windows.
On Windows,
getErrorMessage()constructs astd::stringfromlpMsgBufwithout checking whetherFormatMessageactually wrote to it. WhenFormatMessagefails,lpMsgBufis still uninitialized andstd::string's constructor runsstrlenon an indeterminate stack value.It also leaks the
LocalAlloc'd buffer on the success path.Description
src/backend/common/module_loading_windows.cpp:31-41:FormatMessagereturns 0 and does not touch the output pointer when the message cannot be formatted — which happens for error codes with no entry in the system message table (common for loader and NTSTATUS-derived codes, and for codes left behind by a DLL whoseDllMainfailed). The return value is discarded here, solpMsgBufis then dereferenced regardless.FORMAT_MESSAGE_ALLOCATE_BUFFERalso requires the caller toLocalFreethe buffer; that never happens, so every successful call leaks.Why this is on a hot path
getErrorMessage()is called on every failedLoadLibraryduring backend probing,src/api/unified/symbol_manager.cpp:AF_TRACEevaluates its arguments unconditionally — the spdlog level filter applies after the call — so this runs even with tracing disabled. The unified loader tries roughly a dozen path prefixes across each backend, so on a typical process start most of those attempts fail and this function runs tens of times before any user code executes.Other callers include
src/backend/cuda/cusparseModule.cpp,src/backend/cuda/cudnnModule.cpp, andsrc/backend/common/graphics_common.cpp.Impact
Reading an indeterminate pointer produces either a garbage error string or
STATUS_ACCESS_VIOLATION(0xC0000005), during library initialization and before any user code runs. Intermittent, since it depends on the stack residue at that address.Reproducible Code and/or Steps
System Information
Checklist
Disclaimer
Found by Claude Opus 5. The prompt was to look for potential sources for 0xC0000005 errors on Windows.