Why don’t we allow stacks to be sparse, instead of forcing them to be contiguous?

The Old New Thing (Raymond Chen) News

Summary

The article discusses why stacks are made contiguous in memory instead of sparse, highlighting security risks such as Stack Clash and implementation complexities in exception handling.

<p>When I discussed <a title="Why don't we just make the entire stack out of guard pages?" href="https://devblogs.microsoft.com/oldnewthing/20260713-00/?p=112528"> why we don&#8217;t just make the entire stack out of guard pages</a>, <a href="https://devblogs.microsoft.com/oldnewthing/20260713-00/?p=112528&amp;commentid=144516#comment-144516"> commenter BCS wondered</a>, &#8220;Why require that the stack use contiguously mapped pages? What would break if only touched pages got mapped in? That could actually be a good thing for example with a function that wanted to <code>alloca</code> 512MB on the stack but only read/writes a few pages.&#8221;</p> <p>So the question is asking why the stack must be contiguous. Why not let it be sparse and fault in only the pages that are touched?</p> <p>The first issue is that the stack check code would have to include an explicit check against the stack limit, instead of just walking down the stack a page at a time. This explicit check is needed to avoid security vulnerabilities if somebody manages to <code>alloca</code> a buffer so large that it goes past the end of the stack reservation entirely. If you go a single page at a time, you will eventually hit the no-access page that marks the end of the stack. But if you can leap over multiple pages at a time without touching them, you might leap so far past the end of the stack that you land somewhere else and start corrupting that other memory because you&#8217;re using it as a stack. In linux circles, this vulnerability is nicknamed &#8220;<a href="https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt">Stack Clash</a>&#8220;¹ and goes more formally by &#8220;<a href="https://lwn.net/Articles/725832/">stack guard-page hopping</a>.&#8221;²</p> <p>After fixing that issue, you have another problem: How would you report a failure to commit a page in the middle of the stack?</p> <pre>void dosomething() { void* buffer = NULL; __try { buffer = alloca(65536); } __except (GetExceptionCode() == STATUS_STACK_OVERFLOW) { if (!_resetstkoflw()) __fastfail(FAST_FAIL_FATAL_APP_EXIT); } if (buffer != NULL) { ⟦ use the buffer ⟧ } } </pre> <p>If you allowed sparse stacks, then the memory for the <code>buffer</code> would not actually be committed until the code used it. But the point the code uses the buffer is <i>outside</i> the exception handler for the failed <code>alloca()</code>. The code assumes, not unreasonably, that if <code>alloca</code> succeeds, then the memory is indeed allocated.</p> <p>I guess you could fix this by committing the memory without making it present. That would mean making a call to <code>Virtual­Alloc</code> to expand the stack rather than just accessing the memory. Not only would this make the stack expansion code more complicated, particularly since <a title="Windows stack limit checking retrospective, follow-up" href="https://devblogs.microsoft.com/oldnewthing/20260617-00/?p=112436"> you have to preserve all the registers that might possibly be used by any calling convention</a>, but you also have to make sure that the <code>Virtual­Alloc</code> function itself doesn&#8217;t allocate too much stack!</p> <p>Now, you can still tweak the x86-32 stack prober to avoid <a href="https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&amp;commentid=143917#comment-143917"> pete.d</a>&#8216;s problem, where a large stack frame is made completely present, with the resulting page-ins creating noticeable performance issues. The x86-32 prober could short-circuit the stack probe (<a title="Windows stack limit checking retrospective: arm64, also known as AArch64" href="https://devblogs.microsoft.com/oldnewthing/20260320-00/?p=112154">like the MIPS and other processors listed in the table on this page</a>) so that the page-ins occur only when the stack is actually expanding.</p> <p>¹ Bonus reading about Stack Clash:</p> <ul> <li><a href="https://developers.redhat.com/blog/2017/09/25/stack-clash-mitigation-gcc-background"> Stack Clash Mitigation in GCC &#8212; Background</a></li> <li><a href="https://developers.redhat.com/blog/2019/04/30/stack-clash-mitigation-in-gcc-why-fstack-check-is-not-the-answer"> Stack Clash mitigation in GCC: Why -fstack-check is not the answer</a></li> <li><a href="https://developers.redhat.com/blog/2020/05/22/stack-clash-mitigation-in-gcc-part-3"> Stack clash mitigation in GCC, Part 3</a></li> </ul> <p>² Some systems mitigate stack guard-page hopping by creating a really large no-access region beyond the end of the stack. However, this isn&#8217;t a fix; just a mitigation. It just makes people have to leap further to clear the no-access region. If you already have this vulnerability, it&#8217;s probably because an attacker can control the size of the allocation, in which case you didn&#8217;t really slow them down by much; they just have to put a bigger number in their attack payload.</p> <p>Other systems address this more thoroughly by (surprise) probing each page of the stack in sequence.</p> <p>Stack Clash continues to be a problem even though gcc had a solution in 2020. <a href="https://app.opencve.io/cve/CVE-2026-77658"> Here&#8217;s CVE-2026-77658 from just a few days ago</a>.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260907-00/?p=112677">Why don&#8217;t we allow stacks to be sparse, instead of forcing them to be contiguous?</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: 09/08/26, 11:52 PM

# Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260907-00?p=112677](https://devblogs.microsoft.com/oldnewthing/20260907-00?p=112677) When I discussed[why we don’t just make the entire stack out of guard pages](https://devblogs.microsoft.com/oldnewthing/20260713-00/?p=112528),[commenter BCS wondered](https://devblogs.microsoft.com/oldnewthing/20260713-00/?p=112528&commentid=144516#comment-144516), “Why require that the stack use contiguously mapped pages? What would break if only touched pages got mapped in? That could actually be a good thing for example with a function that wanted to`alloca`512MB on the stack but only read/writes a few pages\.” So the question is asking why the stack must be contiguous\. Why not let it be sparse and fault in only the pages that are touched? The first issue is that the stack check code would have to include an explicit check against the stack limit, instead of just walking down the stack a page at a time\. This explicit check is needed to avoid security vulnerabilities if somebody manages to`alloca`a buffer so large that it goes past the end of the stack reservation entirely\. If you go a single page at a time, you will eventually hit the no\-access page that marks the end of the stack\. But if you can leap over multiple pages at a time without touching them, you might leap so far past the end of the stack that you land somewhere else and start corrupting that other memory because you’re using it as a stack\. In linux circles, this vulnerability is nicknamed “[Stack Clash](https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt)“¹ and goes more formally by “[stack guard\-page hopping](https://lwn.net/Articles/725832/)\.”² After fixing that issue, you have another problem: How would you report a failure to commit a page in the middle of the stack? ``` void dosomething() { void* buffer = NULL; __try { buffer = alloca(65536); } __except (GetExceptionCode() == STATUS_STACK_OVERFLOW) { if (!_resetstkoflw()) __fastfail(FAST_FAIL_FATAL_APP_EXIT); } if (buffer != NULL) { ⟦ use the buffer ⟧ } } ``` If you allowed sparse stacks, then the memory for the`buffer`would not actually be committed until the code used it\. But the point the code uses the buffer is*outside*the exception handler for the failed`alloca\(\)`\. The code assumes, not unreasonably, that if`alloca`succeeds, then the memory is indeed allocated\. I guess you could fix this by committing the memory without making it present\. That would mean making a call to`Virtual­Alloc`to expand the stack rather than just accessing the memory\. Not only would this make the stack expansion code more complicated, particularly since[you have to preserve all the registers that might possibly be used by any calling convention](https://devblogs.microsoft.com/oldnewthing/20260617-00/?p=112436), but you also have to make sure that the`Virtual­Alloc`function itself doesn’t allocate too much stack\! Now, you can still tweak the x86\-32 stack prober to avoid[pete\.d](https://devblogs.microsoft.com/oldnewthing/20260311-00/?p=112134&commentid=143917#comment-143917)‘s problem, where a large stack frame is made completely present, with the resulting page\-ins creating noticeable performance issues\. The x86\-32 prober could short\-circuit the stack probe \([like the MIPS and other processors listed in the table on this page](https://devblogs.microsoft.com/oldnewthing/20260320-00/?p=112154)\) so that the page\-ins occur only when the stack is actually expanding\. ¹ Bonus reading about Stack Clash: - [Stack Clash Mitigation in GCC — Background](https://developers.redhat.com/blog/2017/09/25/stack-clash-mitigation-gcc-background) - [Stack Clash mitigation in GCC: Why \-fstack\-check is not the answer](https://developers.redhat.com/blog/2019/04/30/stack-clash-mitigation-in-gcc-why-fstack-check-is-not-the-answer) - [Stack clash mitigation in GCC, Part 3](https://developers.redhat.com/blog/2020/05/22/stack-clash-mitigation-in-gcc-part-3) ² Some systems mitigate stack guard\-page hopping by creating a really large no\-access region beyond the end of the stack\. However, this isn’t a fix; just a mitigation\. It just makes people have to leap further to clear the no\-access region\. If you already have this vulnerability, it’s probably because an attacker can control the size of the allocation, in which case you didn’t really slow them down by much; they just have to put a bigger number in their attack payload\. Other systems address this more thoroughly by \(surprise\) probing each page of the stack in sequence\. Stack Clash continues to be a problem even though gcc had a solution in 2020\.[Here’s CVE\-2026\-77658 from just a few days ago](https://app.opencve.io/cve/CVE-2026-77658)\. ### 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

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

The Old New Thing (Raymond Chen)

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.

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.

Multistack Concatenative Programming Languages

Lobsters Hottest

An exploration of multistack concatenative programming languages, discussing how auxiliary stacks and dynamic bindings like Factor's namespaces and PostScript's dictionaries can ease data stack management without sacrificing concatenative composition.