A double free occurs when a program attempts to deallocate the same memory address twice. This corrupts the memory allocator's internal bookkeeping, potentially allowing an attacker to overwrite arbitrary memory or execute code. Double frees are most common in C and C++ applications that manually manage memory.
02How It Happens
Memory allocators (like malloc and free) maintain metadata about allocated and freed blocks to track which regions are available for reuse. When free() is called on a pointer, the allocator marks that block as available and updates its internal structures. If the same pointer is freed again without being reallocated in between, the allocator's metadata becomes inconsistent—the same block may be marked as both free and in-use, or linked into the free list twice. This corruption can be exploited to write controlled data into heap structures, potentially overwriting function pointers, virtual method tables, or other critical data.
The root cause is typically a logic error: a pointer is freed in one code path, then freed again in another (often in cleanup or error-handling code), or a pointer is freed and then dereferenced or freed again without being set to NULL.
03Real-World Impact
Successful exploitation of a double free can lead to heap corruption, denial of service (crash), or arbitrary code execution. An attacker who can trigger a double free and control the timing and content of subsequent allocations may be able to overwrite sensitive data structures in memory, hijack control flow, or execute arbitrary code with the privileges of the affected process.
04Vulnerable & Fixed Patterns
Vulnerable pattern
void process_data(char *buffer) {
char *temp = malloc(256);
strcpy(temp, buffer);
if (strlen(temp) > 100) {
free(temp);
return; // Early exit
}
// ... more processing ...
free(temp); // Double free if early exit was taken
}
Why it's vulnerable: The pointer temp is freed on the early-exit path, but the function also frees it at the end. If the condition is met, free() is called twice on the same address.
<?php
function handle_resource($resource_id) {
$resource = open_resource($resource_id);
if (!validate_resource($resource)) {
close_resource($resource);
return false;
}
process_resource($resource);
close_resource($resource); // May close twice
}
?>
Why it's vulnerable: If validate_resource() fails, the resource is closed, but close_resource() is called again at the end. Depending on the implementation, this could corrupt internal state or cause a crash.
Fixed pattern
<?php
function handle_resource($resource_id) {
$resource = open_resource($resource_id);
if (!validate_resource($resource)) {
close_resource($resource);
return false; // Exit early; don't reach the second close
}
process_resource($resource);
close_resource($resource);
}
?>
05Prevention Checklist
Use automatic memory management — prefer garbage-collected languages or smart pointers (std::unique_ptr, std::shared_ptr in C++) over manual malloc/free.
Set pointers to NULL after freeing — this makes accidental double frees safer (though not a complete fix).
Centralize resource cleanup — use RAII (Resource Acquisition Is Initialization) patterns or try-finally blocks to ensure cleanup happens exactly once.
Code review error paths — carefully trace all exit points (early returns, exceptions, error conditions) to confirm each resource is freed exactly once.
Use static analysis tools — tools like Clang Static Analyzer, Valgrind, or AddressSanitizer can detect double frees during development.
Avoid manual pointer passing — encapsulate resource management in classes or functions with clear ownership semantics.
06Signs You May Already Be Affected
Crashes or segmentation faults in production, especially in cleanup or error-handling code paths, may indicate a double free. Heap corruption warnings from memory debugging tools (Valgrind, AddressSanitizer) are a strong signal. Unexplained memory corruption or heap metadata errors in logs should be investigated.