Memory Safety / C & C++

Buffer Overflow, Underflow & Use-After-Free

Three ways a program can access memory it shouldn't.

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.

Analogy: A car park has 5 spaces. 8 cars arrive. The extra 3 spill onto the pavement, blocking roads — or in memory terms, corrupting other data or code nearby.
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.

Analogy: A conveyor belt machine expects 10 items but only 5 arrive. It reaches back before the belt's start and grabs whatever junk happens to be there.
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.

Analogy: You check out of a hotel room and hand back the key — but then walk back in anyway. Someone else may have already moved in. You're rummaging through their belongings, or worse, they're watching you.
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.