A buffer overflow is a spatial bug: you write outside the bounds of an object that is still valid. A use-after-free is a temporal bug, and the distinction is what makes it the dominant memory-safety class in modern browsers and kernels. The object is exactly the right size and the pointer to it is well-formed. The only thing wrong is time. The memory was handed back to the allocator, and the program kept a pointer to it anyway.
The idea
A use-after-free is dangerous not because the free happened but because of what happens between the free and the next dereference. Once memory is released, the allocator is free to hand that same region to a later allocation. An attacker who can trigger a reallocation of controlled size and content now owns the bytes the stale pointer still trusts. The dangling pointer has not moved; the meaning of what it points at has changed underneath it.
The dangling pointer
The precondition is a pointer that outlives the object it names. Wikipedia’s definition is structural: dangling pointers are “pointers that do not point to a valid object of the appropriate type.” Freeing an object does not zero the pointers to it, so those pointers keep their old address while the storage they name is returned to the pool. Dereferencing one is undefined behavior: “if the program then dereferences the (now) dangling pointer, unpredictable behavior may result.” The specific name for the exploitable case is precise: “When a dangling pointer is used after it has been freed without allocating a new chunk of memory to it, this becomes known as a ‘use after free’ vulnerability.”
Reallocation is the exploit primitive
The crash case is the boring case. The exploit case is the reallocation case: “The system may reallocate the previously freed memory, and if the program then dereferences the (now) dangling pointer, unpredictable behavior may result, as the memory may now contain completely different data.” An attacker who can drive an allocation of the right size class into the just-freed slot chooses what “completely different data” means. This is heap grooming: shaping the allocator’s free lists so the freed object’s slot is refilled with an attacker-crafted object before the dangling pointer is used again.
From stale read to hijacked control flow
What elevates a controlled reallocation to arbitrary code execution is the same lever as a stack smash, one trusted pointer the CPU follows. In C++, objects with virtual methods carry a vtable pointer at a fixed offset. If the freed object was such an object and the attacker refills its slot, “if the pointer is used to make a virtual function call, a different address (possibly pointing at exploit code) may be called due to the vtable pointer being overwritten.” A write through the dangling pointer is equally useful the other direction: “if the pointer is used for writing to memory, some other data structure may be corrupted.” Either way the read or write lands in memory the attacker now controls the layout of.
Why the defenses look different from stack defenses
Stack canaries and non-executable stacks answer spatial overflows. They do nothing for a temporal bug where every access is in-bounds. Use-after-free is why the industry moved to allocator hardening (partitioned heaps, quarantine and delayed reuse, pointer authentication) and, ultimately, to languages whose type systems forbid the dangling reference outright. The bug is a lifetime error, so the durable fix is a lifetime discipline the compiler enforces, not a runtime tripwire.
Related Notes
- Buffer Overflows, the spatial counterpart this bug is deliberately contrasted against
- Memory Protections: ASLR, DEP, and Stack Canaries, defenses aimed at overflows that a temporal bug sidesteps
- Control-Flow Integrity, the mitigation that targets the hijacked virtual call directly
- Return-Oriented Programming, how attackers turn one controlled pointer into full execution without injecting code
Sources
- “Dangling pointer,” Wikipedia. https://en.wikipedia.org/wiki/Dangling_pointer . Supports that dangling pointers are “pointers that do not point to a valid object of the appropriate type,” that dereferencing after free gives “unpredictable behavior,” that “the system may reallocate the previously freed memory,” that a corrupted vtable pointer can redirect a virtual call to exploit code, and the definition of a “use after free” vulnerability.