Why don’t we just make the entire stack out of guard pages?

The Old New Thing (Raymond Chen) News

Summary

The article explains why making the entire stack out of guard pages is problematic, as it could lead to unbounded memory allocation and system hangs. Instead, fixed guard pages are preferred for bounded fault handling.

<p>In my earlier overview of <a title="How do compilers ensure that large stack allocations do not skip over the guard page?" href="https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134"> how compilers on different architectures perform stack probes</a>, <a href="https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&amp;commentid=143919#comment-143919"> Cole Tobin asked</a>, &#8220;Why not have a page fault handler that detects the faulting address being the stack and page in the other pages?&#8221;</p> <p><a href="https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&amp;commentid=143920#comment-143920"> Csaba Varga replied</a>, &#8220;My guess: you don&#8217;t want an invalid pointer dereference to allocate a huge chunk of stack, just because the pointer happens to be pointing where the stack might grow, eventually. You want an invalid pointer dereference to segfault most of the time.&#8221;</p> <p>I agree with Csaba on this.</p> <p>If the entire stack were made of guard pages, then it means that a single page fault far below the stack limit could take arbitrary long and allocate arbitrarily large quantities of memory. The program might have said that it wants stacks to default to 1GB, and now a single page fault on the stack could result in a long pause as the system allocates 1GB of memory. If you study the problem in the debugger, what you see is that a single memory read takes several minutes.</p> <p>And even worse is that there&#8217;s no way to stop it, since it&#8217;s happening in kernel mode. You see a program starting to balloon and consume all the memory in the system, so you go to Task Manager and terminate it, but the process doesn&#8217;t die. It just keeps on growing!</p> <p>Even if the guard page is more than one page, it&#8217;s still a small fixed number of pages, the system can satisfy a guard page fault in a short amount of time. And more importantly, the amount of work is bounded.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260713-00/?p=112528">Why don&#8217;t we just make the entire stack out of guard pages?</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 07/13/26, 10:49 PM

# Why don't we just make the entire stack out of guard pages? - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260713-00?p=112528](https://devblogs.microsoft.com/oldnewthing/20260713-00?p=112528) In my earlier overview of[how compilers on different architectures perform stack probes](https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134),[Cole Tobin asked](https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&commentid=143919#comment-143919), “Why not have a page fault handler that detects the faulting address being the stack and page in the other pages?” [Csaba Varga replied](https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&commentid=143920#comment-143920), “My guess: you don’t want an invalid pointer dereference to allocate a huge chunk of stack, just because the pointer happens to be pointing where the stack might grow, eventually\. You want an invalid pointer dereference to segfault most of the time\.” I agree with Csaba on this\. If the entire stack were made of guard pages, then it means that a single page fault far below the stack limit could take arbitrary long and allocate arbitrarily large quantities of memory\. The program might have said that it wants stacks to default to 1GB, and now a single page fault on the stack could result in a long pause as the system allocates 1GB of memory\. If you study the problem in the debugger, what you see is that a single memory read takes several minutes\. And even worse is that there’s no way to stop it, since it’s happening in kernel mode\. You see a program starting to balloon and consume all the memory in the system, so you go to Task Manager and terminate it, but the process doesn’t die\. It just keeps on growing\! Even if the guard page is more than one page, it’s still a small fixed number of pages, the system can satisfy a guard page fault in a short amount of time\. And more importantly, the amount of work is bounded\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.

Similar Articles

Windows stack limit checking retrospective, follow-up

The Old New Thing (Raymond Chen)

Raymond Chen follows up on his previous article about stack limit checking on ARM64, addressing a detail about the unconventional use of the x15 register in stack probe functions and comparing register usage across multiple architectures.

Memory Safety's Hardest Problem

matklad

This article discusses a fundamental memory safety challenge involving tagged unions where a pointer to one variant is used after the union is overwritten with a different variant, leading to type confusion. The author also argues that buffer overflows are the most exploitable memory error and could have been mitigated with better array syntax.

Eliminating Go bound checks with unsafe

Lobsters Hottest

The article explains how to use unsafe pointer arithmetic in Go to eliminate bound checks that the compiler cannot remove, improving performance in hot paths. It discusses the overhead of bound checks and conventional BCE before introducing the unsafe approach.