CWE-415 is the case where a product calls free() twice on the same memory address. It is a memory safety bug, found in memory unsafe languages such as C and C++.
If you write or depend on native code, it applies to you. That includes parsers, network daemons, drivers and the native extensions that sit under higher level languages.
How it happens
The allocator keeps bookkeeping data about freed chunks. Freeing the same chunk twice corrupts that bookkeeping. Later allocations can then return overlapping memory, which gives an attacker a way to read or write places they should not.
The cause is almost always a messy ownership story. Two pieces of code both believe they own a buffer. Error paths are the usual culprit, because cleanup code runs on a path the author did not test.
/* Vulnerable: error path frees buf, then cleanup frees it again */
int process(const char *input) {
char *buf = malloc(256);
if (!buf) return -1;
if (parse(input, buf) < 0) {
free(buf); /* first free */
goto cleanup;
}
/* ... normal work ... */
cleanup:
free(buf); /* second free on the error path */
return 0;
}
MITRE's observed examples fit this shape. Several are double frees resulting from certain error conditions (CVE-2004-0642, CVE-2004-0772, CVE-2005-1689). Another is a chain where a signal handler contained too much functionality, introducing a race condition that led to a double free (CVE-2006-5051).
What an attacker gains
The record lists modifying memory and executing unauthorized code or commands, with impact on integrity, confidentiality and availability. When the memory management data structures are corrupted, an attacker may read or modify unexpected memory addresses. At minimum, the process crashes.
How to find it
MITRE lists three detection approaches:
- Fuzzing. Feed large numbers of diverse inputs and watch for crashes or memory corruption. Error paths often only trigger on malformed input, which fuzzers are good at producing.
- Automated static analysis. Source or binary analysis can find some instances by modeling data flow, though not all.
- Automated dynamic analysis. Use compile time instrumentation such as AddressSanitizer for C and C++. It reports double frees at runtime. MITRE notes that crafted inputs are needed to reach the code with the error.
A practical combination is to build your test suite with AddressSanitizer and run the fuzzer under it.
gcc -fsanitize=address -g -o app app.c
If the bug lives in a library you depend on instead of your own code, a free dependency scan can show known vulnerable versions in your manifest. It runs in the browser and the file stays on your machine. Reachability analysis then helps you see whether your code actually calls the affected function.
How to fix it
The MITRE record does not list formal mitigations for this entry, so these are standard practices.
- One owner per allocation. Decide which function frees each buffer and write it down in a comment.
- Null the pointer after freeing.
free(NULL)is defined as a no-op, so this turns a double free into a harmless call. - Single exit path. Use one cleanup label and free each resource there once.
- Avoid sharing raw pointers. In C++, use
std::unique_ptrfor single ownership andstd::shared_ptronly when ownership is truly shared. - Keep signal handlers tiny. Do not free memory inside them.
/* Fixed: single cleanup point, pointer cleared after free */
int process(const char *input) {
int rc = -1;
char *buf = malloc(256);
if (!buf) return -1;
if (parse(input, buf) < 0)
goto cleanup;
/* ... normal work ... */
rc = 0;
cleanup:
free(buf);
buf = NULL;
return rc;
}
Quick check
Why does nulling the pointer help?
Calling free on a null pointer does nothing by definition, so a second call on the same variable is safe.
Where do double frees usually hide?
In error handling and cleanup paths that are rarely exercised in testing, and in code where two owners share a pointer.
Do higher level languages avoid this?
Memory safe languages do not expose free, so this bug does not occur in your own code there. Native dependencies underneath can still have it.