TL;DR
Buffer overflows happen when a program writes data beyond the allocated space for a buffer. The stack and heap are two different areas of memory where these can occur, but they’re exploited differently. Stack overflows are generally easier to exploit (e.g., overwriting return addresses) but more predictable. Heap overflows are harder to exploit but offer greater control once successful.
Understanding the Stack
The stack is a region of memory used for storing local variables, function arguments, and return addresses during program execution. It follows a Last-In, First-Out (LIFO) principle. When a function is called, a ‘stack frame’ is created.
Understanding the Heap
The heap is used for dynamic memory allocation – when you request memory at runtime using functions like malloc in C or new in C++. Unlike the stack, the heap doesn’t have a strict LIFO order. Memory blocks are allocated and deallocated as needed.
1. Stack Buffer Overflows
- How they happen: A stack buffer overflow occurs when you write more data into a local variable (buffer) on the stack than it can hold. This overwrites adjacent memory locations, potentially including the return address.
- Example (C):
#include <stdio.h> #include <string.h> int main() { char buffer[8]; strcpy(buffer, "This is a very long string"); // Vulnerable! printf("%sn", buffer); return 0; }In this example,
strcpydoesn’t check the size of the input string. If it’s longer than 7 characters (plus null terminator), it will overwrite other data on the stack. - Exploitation: Attackers often overwrite the return address with an address pointing to malicious code (shellcode). When the function returns, instead of going back to its caller, it jumps to the shellcode.
- Mitigation:
- Use safe string functions like
strncpyorsnprintfwhich limit the number of characters copied. - Enable stack canaries (compiler feature) – a random value placed on the stack before the return address; overwritten canary indicates an overflow.
- Address Space Layout Randomization (ASLR) makes it harder to predict where code and data are located in memory.
- Use safe string functions like
2. Heap Buffer Overflows
- How they happen: A heap buffer overflow occurs when you write beyond the allocated size of a dynamically allocated buffer on the heap. This can corrupt metadata used by the memory allocator, or overwrite other heap-allocated data structures.
- Example (C):
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *buffer = (char *)malloc(8); strcpy(buffer, "This is a very long string"); // Vulnerable! printf("%sn", buffer); free(buffer); return 0; }Similar to the stack example,
strcpydoesn’t perform bounds checking. - Exploitation: Heap exploitation is more complex than stack exploitation.
- Metadata Corruption: Overwriting heap metadata can allow an attacker to control future memory allocations and deallocations.
- Chunk Manipulation: Attackers manipulate heap chunks (allocated blocks of memory) to achieve arbitrary code execution. Techniques include use-after-free, double-free, and others.
- Mitigation:
- Use safe allocation functions or libraries that provide bounds checking.
- Implement heap hardening techniques (e.g., guard pages, metadata integrity checks).
- ASLR is crucial for making heap exploitation more difficult.
- Consider using memory-safe languages like Rust or Go which have built-in protection against buffer overflows.
3. Key Differences Summarised
| Feature | Stack Overflow | Heap Overflow |
|---|---|---|
| Memory Management | Automatic (LIFO) | Dynamic (malloc/new) |
| Exploitation Difficulty | Generally easier | More complex |
| Predictability | More predictable | Less predictable |
| Common Exploits | Return address overwrites | Metadata corruption, chunk manipulation |

