A memory leak starts as a reliability bug and becomes a security bug when someone else controls how often it happens. CWE-401 is the weakness where the product does not sufficiently track and release allocated memory after use, making that memory unavailable for reallocation and reuse.
It applies mainly to C and C++ and other code that manages memory by hand. Long running services, daemons and kernels are the most exposed, because a small leak per request adds up over days.
How it happens
The usual causes are an early return that skips the free call, a counter or reference count that is not decremented, or freeing the outer object but not what it owns. The record includes real examples:
- CVE-2005-3119: a memory leak because a function does not free() an element of a data structure.
- CVE-2004-0427: a memory leak when a counter variable is not decremented.
- CVE-2002-0574: a chain where a reference count is not decremented, leading to a leak in an OS when ICMP packets are sent.
- CVE-2005-3181: a kernel uses the wrong function to release a data structure, so data is not tracked properly by other code.
A short C example of the early return leak:
int handle(const char *path) {
char *buf = malloc(4096);
if (!buf) return -1;
FILE *f = fopen(path, "r");
if (!f) return -1; // buf is never freed on this path
fread(buf, 1, 4096, f);
fclose(f);
free(buf);
return 0;
}
Each call with a missing file leaks 4 KB. A client who can request missing files in a loop can exhaust memory.
What an attacker gains
MITRE lists availability impact: crash, exit or restart, instability, and CPU or memory consumption. It also lists reduced performance. Most leaks only hurt reliability, but if an attacker can deliberately trigger the leak, they can mount a denial of service by depleting memory. The ICMP case above is exactly that: network traffic alone drives the leak.
How to find it
MITRE names several techniques:
- Fuzzing, which generates large numbers of diverse inputs and can expose leaks that normal use never reaches.
- Dynamic analysis with compiler integrated runtime checks, such as AddressSanitizer (ASan) for C and C++, which can report leaks at exit.
- Static analysis (SAST), which can find some instances by analyzing source code.
Add a soak test: run the service under steady load for hours and watch resident memory. A line that only goes up is a leak. If you can, send malformed and error producing requests too, since error paths are where leaks hide.
How to fix it
MITRE's mitigations, in plain terms:
- Choose a language or tool with automatic memory management, or one that makes manual management less error prone. MITRE mentions automatic reference counting (ARC) when targeting Apple platforms, and smart pointers such as unique_ptr and shared_ptr in C++.
- Use an abstraction library to hide risky APIs. MITRE notes this is not a complete solution.
- Consider a garbage collector such as Boehm-Demers-Weiser for C and C++. MITRE rates it as moderately effective and says it is not 100 percent effective.
In code, a single exit point makes it hard to forget:
int handle(const char *path) {
int rc = -1;
char *buf = malloc(4096);
FILE *f = NULL;
if (!buf) goto out;
f = fopen(path, "r");
if (!f) goto out;
fread(buf, 1, 4096, f);
rc = 0;
out:
if (f) fclose(f);
free(buf);
return rc;
}
In C++, let the type do the work:
auto buf = std::make_unique<char[]>(4096); // freed on every exit path
Also match allocators and deallocators (malloc with free, new with delete), and release owned members when you free a composite object.
Quick check
When does a leak become a security issue?
When an attacker can trigger it repeatedly. Then it becomes a denial of service by memory exhaustion.
Which tool reports leaks at runtime?
MITRE names AddressSanitizer for C and C++ among compiler integrated runtime checks.