From ab50f30bb10eee23cedc467e088f7749f0948004 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 4ee7ae8e6575a..5079172020226 100644
--- a/src/stdlib/SDL_malloc.c
+++ b/src/stdlib/SDL_malloc.c
@@ -6330,7 +6330,125 @@ int mspace_mallopt(int param_number, int value) {
#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); }