CWE-772 is the classic resource leak. The product does not release a resource after its effective lifetime has ended, meaning after the resource is no longer needed.
Long running services feel it most, because small leaks accumulate. MITRE's platform listing for this entry is Mobile, but the pattern appears everywhere.
How it happens
Code acquires a socket, file, connection or block of memory, then leaves a path that skips the release. The common culprit is an error path or early return. The record's examples show how an attacker uses it:
- CVE-2007-0897: a chain where an anti-virus product met a malformed file and returned from a function without closing a file descriptor, leading to descriptor consumption and failed scans.
- CVE-2001-0830: sockets were not properly closed when an attacker repeatedly connected and disconnected.
- CVE-1999-1127: named pipe connections were not shut down when malformed data was sent.
- CVE-2009-2858: a chain where a memory leak led to resource exhaustion.
Notice that most triggers are malformed input. Attackers feed the error path on purpose because they know it is the least tested.
# Vulnerable: the early return skips close()
def scan(path):
f = open(path, "rb")
header = f.read(4)
if header != b"MAGIC":
return False # file handle leaks here
result = deep_scan(f)
f.close()
return result
What an attacker gains
The record lists availability: resource consumption of memory, CPU and other resources. An attacker who can influence allocation that is never released can deplete the pool and prevent all other processes from using the same type of resource. No privileges or code execution are required, just repetition.
How to find it
MITRE says static analysis can find some instances through data flow modeling. Complement it with runtime observation:
- Run a soak test and chart open handles, sockets and memory over time.
- Send malformed input repeatedly and compare counts before and after.
- Review every function that acquires something, and trace each exit, particularly error returns.
A count that rises and never falls back to baseline is the signal.
How to fix it
MITRE's mitigations, as steps:
- Choose languages with automatic memory management where you can. Java, Ruby and Lisp collect garbage that releases memory for deallocated objects. This covers memory, though other resources like sockets still need care.
- Own your releases. Free every resource you allocate, and be consistent about how and where you do so inside a function. If you allocate something to free at the end, free it at all exit points, error conditions included.
- Use operating system limits. On POSIX, setrlimit() and getrlimit() can cap and inspect resources, though not every OS supports them. As usage nears the cap (see CWE-770), limit further allocation to privileged users, or release resources held by less privileged ones. MITRE cautions this protects the system but does not necessarily stop an attacker from hurting other users.
- Handle unavailable resources with proper error checks and handling.
The reliable pattern is to tie release to scope so it cannot be skipped:
# Fix: the with block closes the file on every exit
def scan(path):
with open(path, "rb") as f:
if f.read(4) != b"MAGIC":
return False
f.seek(0)
return deep_scan(f)
In C, use a single cleanup label that every path jumps to. In C++, use RAII types. In Go, defer the close immediately after a successful open.
Quick check
Where do leaks usually hide?
In error paths and early returns, which skip the release at the bottom of the function.
Does garbage collection solve it completely?
It helps with memory. Sockets, files and connections often still need explicit release.
Does a resource limit fix a leak?
No. It contains the damage to the system, but a leaking process still fails, and attackers can still affect other users.