Docs / Stack smashing / Memory & stacks

Meet the stack. It keeps receipts.

Understand process memory, automatic storage, stack growth and LIFO behavior before exploring a buffer overflow.

Try the interactive model

On this page

A process gets an address space, not one giant array.

Virtual memory gives a process an address space with mappings and permissions. A useful sketch includes executable code, read-only constants, writable data, dynamically allocated objects, shared libraries and one stack per thread. The paper’s text/data/stack picture is a starting model, not a complete memory map.

Text / code
Machine instructions, typically executable and not writable.
Data and BSS
Storage for initialized and zero-initialized objects with static storage duration. Read-only objects may have separate mappings.
Heap / allocations
Storage returned by an allocator. An allocation is not necessarily a simple extension of the old brk region.
Stack
Call-related storage, local spill slots and saved context, depending on the compiler and ABI.

A local char name[16] has automatic storage duration. It commonly lives in a stack frame, but a compiler can eliminate it or keep some values in registers. A pointer declared inside a function can point to heap storage. “Local,” “dynamic” and “on the stack” are not synonyms.

Last in, first out. No queue-jumping.

Put a receipt on a pile. Put another on top. The second comes off first. That is LIFO: last in, first out. A queue is different: first in, first out. Function calls use this nesting to return to their callers in the right order.

In the model, each PUSH reserves four bytes at a lower address, then stores a value. POP reads the current top and advances the stack pointer by four. Try two pushes followed by one pop. The last value you pushed should be the one returned.

This optional lifo model becomes interactive with JavaScript. The lesson, explanations and checkpoint answer remain readable without it.

The top of the stack means the most recently used end, not the top of the diagram or the largest number. Here, ESP points at that end. The displayed addresses increase downward, so a downward-growing stack expands upward on this particular drawing. Trust the numbers, not the page direction.

A growing stack and an overflowing copy move differently.

Stack growth describes where a new frame or PUSH reserves space. A string copy describes how destination addresses advance through an array. In our model, a new allocation moves ESP toward lower addresses; a forward copy writes buffer[0], buffer[1], buffer[2] at increasing addresses.

That is why a lower-address local buffer can be overrun into higher-address saved context. There is no contradiction. These are two different operations on the same address space.

The kernel also does not promise to pause a process and supply extra memory whenever it runs out. Stacks have limits and guard regions; invalid accesses can fault. Resource exhaustion has several possible outcomes.

An ABI is the agreement everyone follows.

The application binary interface, or ABI, defines how independently compiled code cooperates: argument locations, register responsibilities, alignment and more. The next lesson uses an illustrative 32-bit x86 cdecl-style frame with EBP. It is not the only valid layout.

On common x86-64 System V systems, initial integer/pointer arguments often travel in registers and pointers are eight bytes. Other platforms differ again. Optimizations can omit a dedicated frame pointer, inline a function or avoid a call entirely. Before doing CTF offset arithmetic, identify the actual target architecture and inspect its disassembly.

Your turn. Call the outcome.

This model executes PUSH on a downward-growing four-byte stack. What happens to ESP?

Read the explanation

ESP decreases by four to reserve the next slot. This says nothing about the direction of a later array copy.

Sources & what we changed

Based on the supplied text of Aleph One’s Smashing the Stack for Fun and Profit, Phrack 49, file 14, 8 November 1996. Original author credit remains with Aleph One. This independent OverWatch Labs explanation uses new prose, synthetic models and modern defensive notes; it is not endorsed by Phrack or the original author.

This lesson maps to: Process Memory Organization; What Is A Stack?; Why Do We Use A Stack?; The Stack Region. Historical examples are described, not represented as current product vulnerabilities. We correct byte-addressability, array padding, terminator counting and the supplied return-address arithmetic rather than reproducing those errors.

Original shell-spawning byte arrays and platform-specific exploit drivers are replaced with symbolic models. The causal concepts are preserved; browser outcomes are not native exploitation evidence. Modern controls are editorial additions, not claims made by the 1996 paper.