Double free
| Vulnerability potential | High |
| DDoS potential | Medium |
The memory by the pointer might be freed twice or more times
Impact
Freeing the same block twice (CWE-415) corrupts the allocator’s internal
bookkeeping. Between the two frees the block may have been handed out again to
satisfy a different allocation; the second free then returns that live
block’s memory to the free list, so two parts of the program now believe they
own the same region. The allocator’s free-list links — often stored inside the
freed chunk — get tangled, which leads to crashes (malloc/free aborting with
“double free or corruption”), silent heap corruption, and, in the hands of an
attacker, controlled writes. Like other heap bugs it is timing-dependent and may
appear far from the offending call.
Vulnerability potential
Double-free is a high-severity, actively exploited corruption primitive.
- By controlling the allocations between the two frees, an attacker can make
the free list point where they choose; a subsequent
mallocthen returns a pointer into attacker-influenced memory, yielding an arbitrary-write primitive (the classic “fastbin dup” and related glibc techniques). - That write is commonly leveraged to overwrite a function pointer, GOT entry, or vtable, achieving remote code execution.
- Two live pointers to the same block (the aliasing the second free creates) is itself a use-after-free situation enabling type confusion and info leaks.
- At minimum, the corruption aborts the process — a Denial-of-Service.
Technical details
free(p) records p’s chunk as available, typically by writing free-list
pointers into the chunk body. Calling free(p) again on a pointer that is
already free (or has been re-allocated) writes those bookkeeping pointers a
second time over memory whose state no longer matches, breaking the invariants
malloc relies on.
How it arises
Two code paths both freeing the same pointer; freeing in a loop or error handler
that also runs in the normal path; two aliases to one allocation each freed by
its owner; freeing a realloc‘d pointer and the original; and exception/goto
cleanup that double-runs.
Allocator hardening
Modern glibc detects some cases (“double free or corruption (fasttop)”) and many
allocators (glibc tcache with tcache_double_free detection, hardened_malloc,
Windows LFH) add checks — but these are best-effort and bypassable, not a
substitute for correct code.
The null trick
free(NULL) is defined as a no-op, so setting a pointer to NULL right after
freeing turns an accidental second free into a harmless no-op. Free exactly
once, and clear the pointer.
Catching the issue
AddressSanitizer
-fsanitize=address reports attempting double-free with the stack traces of
both frees and the original allocation — the fastest way to pinpoint it.
Valgrind
Memcheck reports “Invalid free() / delete / delete[]” on the second free, identifying both the bad free and the first one.
Static analysis
Clang Static Analyzer (unix.Malloc), Coverity, Cppcheck, and PVS-Studio find
many double-free paths, especially around error handling.
Design
Single ownership via std::unique_ptr; set pointers to NULL/nullptr after
freeing; never free through more than one alias; centralise cleanup with a
single-exit pattern; avoid mixing manual free with smart-pointer ownership of
the same block.
How to reproduce
Compile with -fsanitize=address and run; ASan reports an attempting
double-free with both free stack traces.
#include <stdlib.h>
int main(void)
{
int *p = malloc(sizeof *p);
*p = 1;
free(p); /* first free — fine */
free(p); /* second free of the same pointer — corruption */
return 0;
}