Demystifying the Red Zone: Optimizing Leaf Functions | zlnvch's blogDemystifying the Red Zone: Optimizing Leaf Functions<br>July 7, 2026 · 11 min<br>Recently I’ve been learning Assembly language. Specifically focused on x86-64, using the GNU Assembler (GAS), in Intel syntax. I guess it’s my way of rebelling against AI: the higher-level they try to take me, the deeper down the stack I dive. If I get pushed any further, I will have no choice but to learn machine code.<br>TL;DR: The Red Zone is a 128-byte region just below the stack pointer that the System V ABI guarantees won’t be clobbered by asynchronous kernel writes (like signal frames). This lets leaf functions skip stack-pointer bookkeeping. In this post I show the optimization in compiled output, then prove the guarantee empirically by triggering a signal and watching which stack bytes survive.
Actually, I’ve been quite enjoying learning how the metal works, I’m not doing it just out of spite. Something that has piqued my interest in the last few days is The Red Zone , which isn’t as scary as it sounds. The Red Zone is a 128-byte “safe area” of an application’s stack memory. It is supported by x86-64 Linux and Mac, and defined in the System V Application Binary Interface (ABI). It is also supported by ARM64 Mac, although not ARM64 Linux.<br>The System V ABI is a set of specifications that dictate object file formats, executable formats, dynamic linking semantics, and processor-specific calling conventions.
Stack memory is used for function-local memory. It is very fast and simple to allocate: just decrement the stack pointer to allocate bytes and increment it to deallocate. While the default maximum stack-size assigned to an application varies from 1-8 MB, depending on the platform and whether it is the main thread or a spawned thread, the kernel does not reserve the entire amount upfront. Instead, it depends upon the stack pointer to know how much stack memory the application is actually using.<br>In modern system architectures, stack memory usually grows downwards (towards lower memory addresses), thus allocating requires (somewhat confusingly) decrementing the stack pointer.
Therefore, while nothing stops you from writing to memory below the stack pointer, the kernel assumes this space is unused and may overwrite it at any time. One such example of this occurring is during POSIX signals, implemented by Linux and Mac. Used to communicate asynchronous events, signals pause the execution of your code mid-function, and run the signal handler, before resuming. For example, the ctrl+c shortcut sends the SIGINT signal. For a full list of signals on Linux see here<br>or run kill -l on your own terminal.<br>While the default behavior of SIGINT is to terminate the program, if you implement a signal handler, you can use it to do a graceful shutdown of your application, or if you like, nothing at all. If your application does not terminate during the signal handler, it resumes the function it was running before the interruption. In order to save the state of the application before the interruption, the kernel stores the signal frame onto the user-space stack. This includes the instruction pointer, CPU flags and registers. Therefore, if you haven’t been diligent with your stack pointer, your program’s memory can be overwritten.<br>Note: SIGINT is not the same as a hardware interrupt, such as keyboard press or timer. While a hardware interrupt also saves the context of the running user-space app onto the stack, it saves it to the kernel stack. However, a signal frame must be saved to the user stack, as the signal handler runs in user-space.
However, systems that implement the Red Zone, provide a 128-byte safety buffer in the user stack. They pledge that when the kernel asynchronously writes to the user stack, it will land below the red zone, leaving the 128 bytes just beneath the stack pointer untouched. Therefore, when coding a leaf function (a function that does not call other functions), you can be lazy and skip the stack bookkeeping, assuming you’re using at most 128 bytes. It must be a leaf function, because otherwise the next function it calls would overwrite the stack.<br>higher addresses<br>▼ (stack grows down)<br>┌────────────────────────────┐<br>│ ... caller's frame ... │<br>├────────────────────────────┤ ← rsp (top of stack)<br>│ │<br>│ RED ZONE (128 bytes) │ safe: the kernel won't touch this<br>│ │<br>├────────────────────────────┤ ← rsp - 128<br>│ signal frame lands here │ clobbered on async delivery<br>│ ... and below ... │<br>└────────────────────────────┘<br>lower addresses
Why does skipping some simple pointer increments and decrements even matter? At first, it might even sound like it’s encouraging sloppiness. But the Red Zone actually allows for an optimization. Imagine a small, yet frequently called function: stack bookkeeping could represent a significant percentage of its total instructions. I used Gemini to help come up...