Added code to detect memory overwrites on Windows

Define WIN32_DETECT_OVERWRITE while building to enable this functionality.
This commit is contained in:
Sam Lantinga
2026-02-13 13:53:25 -08:00
parent e049fff360
commit ab50f30bb1

View File

@@ -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 <windows.h>
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); }