A buffer is a fixed-size block of memory reserved to hold data — a temporary workspace. When a program reads from or writes to memory outside that workspace, things go wrong.
Buffer Overflow
A buffer overflow happens when a program writes more data than a buffer can hold, spilling past its end into adjacent memory.
char name[5]; // buffer holds 5 characters strcpy(name, "Alexander"); // 9 chars written → overflow! // extra bytes overwrite adjacent memory
The bytes that spill over can overwrite other variables, function return addresses, or security-critical data. This is one of the most exploited vulnerabilities in software history — used in stack-smashing and remote code execution attacks.
Buffer Underflow
A buffer underflow (sometimes called underread) happens when a program reads or accesses memory before the start of the buffer — usually via a negative or miscalculated index.
char buf[10] = "Hi"; char c = buf[-1]; // reading before the buffer's start → underflow! // c contains whatever bytes happened to be in memory before buf
This can leak sensitive data from memory regions that precede the buffer, or cause undefined behaviour and crashes.
Use-After-Free
A use-after-free happens when a program frees (deallocates) a block of memory, but then continues to use the pointer that pointed to it. The memory no longer belongs to your program, but you're still reading or writing it.
char *buf = malloc(10); // allocate memory free(buf); // release it back to the allocator buf[0] = 'A'; // use-after-free! memory may belong to something else now
After free(), that memory region can be reallocated to something else — another variable, another object, or attacker-controlled data. Writing to it corrupts that new owner; reading from it leaks their data.
Use-after-free is particularly dangerous because the freed memory often still looks valid for a while, making bugs intermittent and hard to reproduce. Attackers can exploit it deliberately by triggering the free, then getting their own data allocated into that same slot before the vulnerable code reads or writes it — a technique known as heap spraying. It's a top cause of real-world browser vulnerabilities in Chrome, Firefox, and Internet Explorer.
All Three, Side by Side
| Overflow | Underflow | Use-After-Free | |
|---|---|---|---|
| You access… | Memory past the end of your buffer | Memory before the start of your buffer | Memory after freeing your buffer |
| Nature | Spatial — out of bounds | Spatial — out of bounds | Temporal — out of lifetime |
| Common cause | No bounds check on input | Negative or wrong index | Dangling pointer not nulled after free |
| Risk | Code execution, data corruption | Data leakage, crashes | Code execution, data leakage |
| Seen in | C C++ | C C++ | C C++ |
The root cause of all three is the same: no enforcement of memory ownership. Overflow and underflow violate spatial bounds; use-after-free violates temporal bounds. Modern languages like Python, Java, and Rust prevent all three automatically — Rust does so at compile time through its ownership and borrow checker, with no runtime overhead.