From 297d831192b797157851125a71af4deec9a4773c Mon Sep 17 00:00:00 2001
From: Andrew Noyes <andrew.noyes@snowflake.com>
Date: Tue, 19 Apr 2022 11:22:35 -0700
Subject: [PATCH] Put guard pages next to fast alloc memory (#6885)

* Put guard pages next to fast alloc memory

I verified that we can now detect #6753 without creating tons of
threads.

* Use pageSize instead of 4096

* Don't include mmapInternal for windows
---
 flow/FastAlloc.cpp      |  2 +-
 flow/Platform.actor.cpp | 27 +++++++++++++++++++++------
 flow/Platform.h         |  2 +-
 3 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp
index 262d1b017c..dd6f68c542 100644
--- a/flow/FastAlloc.cpp
+++ b/flow/FastAlloc.cpp
@@ -519,7 +519,7 @@ void FastAllocator<Size>::getMagazine() {
 		--g_allocation_tracing_disabled;
 	}
 #endif
-	block = (void**)::allocate(magazine_size * Size, false);
+	block = (void**)::allocate(magazine_size * Size, /*allowLargePages*/ false, /*includeGuardPages*/ true);
 #endif
 
 	// void** block = new void*[ magazine_size * PSize ];
diff --git a/flow/Platform.actor.cpp b/flow/Platform.actor.cpp
index c07ff01bca..4661c0c6ea 100644
--- a/flow/Platform.actor.cpp
+++ b/flow/Platform.actor.cpp
@@ -2037,7 +2037,22 @@ static void enableLargePages() {
 #endif
 }
 
-static void* allocateInternal(size_t length, bool largePages) {
+#ifndef _WIN32
+static void* mmapInternal(size_t length, int flags, bool guardPages) {
+	if (guardPages) {
+		constexpr size_t pageSize = 4096;
+		length += 2 * pageSize; // Map enough for the guard pages
+		void* resultWithGuardPages = mmap(nullptr, length, PROT_READ | PROT_WRITE, flags, -1, 0);
+		mprotect(resultWithGuardPages, pageSize, PROT_NONE); // left guard page
+		mprotect((void*)(uintptr_t(resultWithGuardPages) + length - pageSize), pageSize, PROT_NONE); // right guard page
+		return (void*)(uintptr_t(resultWithGuardPages) + pageSize);
+	} else {
+		return mmap(nullptr, length, PROT_READ | PROT_WRITE, flags, -1, 0);
+	}
+}
+#endif
+
+static void* allocateInternal(size_t length, bool largePages, bool guardPages) {
 
 #ifdef _WIN32
 	DWORD allocType = MEM_COMMIT | MEM_RESERVE;
@@ -2052,31 +2067,31 @@ static void* allocateInternal(size_t length, bool largePages) {
 	if (largePages)
 		flags |= MAP_HUGETLB;
 
-	return mmap(nullptr, length, PROT_READ | PROT_WRITE, flags, -1, 0);
+	return mmapInternal(length, flags, guardPages);
 #elif defined(__APPLE__) || defined(__FreeBSD__)
 	int flags = MAP_PRIVATE | MAP_ANON;
 
-	return mmap(nullptr, length, PROT_READ | PROT_WRITE, flags, -1, 0);
+	return mmapInternal(length, flags, guardPages);
 #else
 #error Port me!
 #endif
 }
 
 static bool largeBlockFail = false;
-void* allocate(size_t length, bool allowLargePages) {
+void* allocate(size_t length, bool allowLargePages, bool includeGuardPages) {
 	if (allowLargePages)
 		enableLargePages();
 
 	void* block = ALLOC_FAIL;
 
 	if (allowLargePages && !largeBlockFail) {
-		block = allocateInternal(length, true);
+		block = allocateInternal(length, true, includeGuardPages);
 		if (block == ALLOC_FAIL)
 			largeBlockFail = true;
 	}
 
 	if (block == ALLOC_FAIL)
-		block = allocateInternal(length, false);
+		block = allocateInternal(length, false, includeGuardPages);
 
 	// FIXME: SevWarnAlways trace if "close" to out of memory
 
diff --git a/flow/Platform.h b/flow/Platform.h
index 020544178f..272c8e3588 100644
--- a/flow/Platform.h
+++ b/flow/Platform.h
@@ -284,7 +284,7 @@ std::string epochsToGMTString(double epochs);
 
 void setMemoryQuota(size_t limit);
 
-void* allocate(size_t length, bool allowLargePages);
+void* allocate(size_t length, bool allowLargePages, bool includeGuardPages);
 
 void setAffinity(int proc);