From ab50f30bb10eee23cedc467e088f7749f0948004 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 13 Feb 2026 13:53:25 -0800 Subject: [PATCH] Added code to detect memory overwrites on Windows Define WIN32_DETECT_OVERWRITE while building to enable this functionality. --- src/stdlib/SDL_malloc.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/src/stdlib/SDL_malloc.c b/src/stdlib/SDL_malloc.c index 4ee7ae8e65..5079172020 100644 --- a/src/stdlib/SDL_malloc.c +++ b/src/stdlib/SDL_malloc.c @@ -6330,7 +6330,125 @@ History: #endif /* !HAVE_MALLOC */ -#ifdef HAVE_MALLOC + +// Define WIN32_DETECT_OVERWRITE if you'd like guard pages around memory allocations on Windows +#if 0 +#define WIN32_DETECT_OVERWRITE +#endif +#ifdef WIN32_DETECT_OVERWRITE + +#include + +typedef struct +{ + PBYTE pAddr; + ULONG ulSize; +} SAFE_HEAP_POINTER; + +static DWORD GetPageSize() +{ + static DWORD page_size; + + if (!page_size) { + SYSTEM_INFO si = { 0 }; + GetSystemInfo(&si); + page_size = si.dwPageSize; + } + return page_size; +} + +static ULONG SDLCALL real_msize(IN void *pPtr) +{ + PBYTE pVirtualAddr = (PBYTE)pPtr; + SAFE_HEAP_POINTER *pSafePtr = (SAFE_HEAP_POINTER *)(pVirtualAddr - sizeof(SAFE_HEAP_POINTER)); + ULONG_PTR rvaOld = (ULONG_PTR)(pSafePtr + 1) - (ULONG_PTR)pSafePtr->pAddr; + return (ULONG)(pSafePtr->ulSize - GetPageSize() - rvaOld); +} + +static void SDLCALL real_free(IN void *pPtr) +{ + if (!pPtr) { + return; + } + + PBYTE pVirtualAddr = (PBYTE)pPtr; + SAFE_HEAP_POINTER *pSafePtr = (SAFE_HEAP_POINTER *)(pVirtualAddr - sizeof(SAFE_HEAP_POINTER)); + ULONG ulOldProtect; + VirtualProtect(pSafePtr->pAddr + pSafePtr->ulSize - GetPageSize(), GetPageSize(), PAGE_READWRITE, &ulOldProtect); + _aligned_free(pSafePtr->pAddr); +} + +static void *SDLCALL real_malloc(IN size_t dwBytes) +{ + DWORD dwTotalBytes = (DWORD)dwBytes + sizeof(SAFE_HEAP_POINTER); + DWORD dwPages = (dwTotalBytes / GetPageSize()) + 1; + DWORD dwAlignedBytesCount = (dwPages + 1) * GetPageSize(); + PBYTE pPtr = (PBYTE)_aligned_malloc(dwAlignedBytesCount, GetPageSize()); + if (!pPtr) { + return NULL; + } + + ZeroMemory(pPtr, dwAlignedBytesCount); + PBYTE pLastPageStart = pPtr + dwPages * GetPageSize(); + ULONG ulOldProtect; + PBYTE pBlock = (PBYTE)(pLastPageStart - dwBytes); + if (!VirtualProtect(pLastPageStart, GetPageSize(), PAGE_READWRITE | PAGE_GUARD, &ulOldProtect)) { + _aligned_free(pPtr); + return NULL; + } + SAFE_HEAP_POINTER *pSafePtr = (SAFE_HEAP_POINTER *)(pBlock - sizeof(SAFE_HEAP_POINTER)); + pSafePtr->pAddr = pPtr; + pSafePtr->ulSize = dwAlignedBytesCount; + return pBlock; +} + +static void *SDLCALL real_calloc(IN size_t dwElements, IN size_t dwElementSize) +{ + PVOID pPtr = real_malloc(dwElements * dwElementSize); + if (pPtr) { + ZeroMemory(pPtr, dwElements * dwElementSize); + } + return pPtr; +} + +static void *SDLCALL real_realloc(IN void *pPtr, IN size_t dwBytes) +{ + if (!pPtr) { + return real_malloc(dwBytes); + } + + PBYTE pVirtualAddr = (PBYTE)pPtr; + SAFE_HEAP_POINTER *pSafePtr = (SAFE_HEAP_POINTER *)(pVirtualAddr - sizeof(SAFE_HEAP_POINTER)); + SAFE_HEAP_POINTER oldPtr = *pSafePtr; + ULONG ulPrevSize = real_msize(pPtr); + if (ulPrevSize == dwBytes) { + return pPtr; + } + + // Start working on the addresses + DWORD dwTotalBytes = (DWORD)dwBytes + sizeof(SAFE_HEAP_POINTER); + DWORD dwNewPages = (dwTotalBytes / GetPageSize()) + 1; + DWORD dwAlignedBytesCount = (dwNewPages + 1) * GetPageSize(); + PBYTE pBlock = 0; + PBYTE pLastPageStart = 0; + if ((dwAlignedBytesCount <= oldPtr.ulSize) && (dwAlignedBytesCount + GetPageSize() >= oldPtr.ulSize)) { + // No need to reallocate memory, the allocated pages R enough + pLastPageStart = pSafePtr->pAddr + dwNewPages * GetPageSize(); + pBlock = (pLastPageStart - dwBytes); + MoveMemory(pBlock, pPtr, min(ulPrevSize, dwBytes)); + pSafePtr = (SAFE_HEAP_POINTER *)(pBlock - sizeof(SAFE_HEAP_POINTER)); + *pSafePtr = oldPtr; + return pBlock; + } + + // Buffer was enlarged or reduced by more than PAGE_SIZE + PBYTE pNew = (PBYTE)real_malloc(dwBytes); + CopyMemory(pNew, pPtr, min(ulPrevSize, dwBytes)); + real_free(pPtr); + return pNew; +} + +#elif defined(HAVE_MALLOC) static void * SDLCALL real_malloc(size_t s) { return malloc(s); } static void * SDLCALL real_calloc(size_t n, size_t s) { return calloc(n, s); } static void * SDLCALL real_realloc(void *p, size_t s) { return realloc(p,s); }