CWE-1091, Use of Object without Invoking Destructor Method, describes a method that accesses an object but does not later invoke the element's associated finalize or destructor method.
It applies to object oriented languages and any code that holds resources: memory, file handles, sockets, database connections, locks. MITRE allows this entry for mapping real vulnerabilities, with careful reading.
How it happens
Languages differ in how cleanup works, but the failure is the same. The code acquires something, uses it, and leaves a path where release never happens. Early returns and exceptions are the usual causes.
# Vulnerable pattern: the file is closed only on the happy path
def read_config(path):
f = open(path)
data = f.read()
if not data:
raise ValueError("empty config") # f is never closed here
f.close()
return data
In a garbage collected language the object is eventually reclaimed, but "eventually" can be far too late for file handles and connections. In C++, where the destructor runs at a known point, the problem appears when objects are allocated on the heap and never deleted.
# Fix: let the language guarantee cleanup
def read_config(path):
with open(path) as f:
data = f.read()
if not data:
raise ValueError("empty config")
return data
// C++: tie the lifetime to a scope with a smart pointer
auto report = std::make_unique<Report>();
report->build();
// destructor runs automatically when report leaves scope
What an attacker gains
MITRE lists reduced performance: memory or other resources are retained longer than necessary. MITRE adds that if an attacker can reach the code, the performance problem might introduce a vulnerability.
The attack is slow starvation. Send many requests that hit the leaking path, and the process runs out of handles, connections or memory. The service slows, then fails, and a restart only resets the clock. No exploit is needed, only repetition.
How to find it
MITRE lists automated static analysis as a detection method. Several practical checks help.
- Static analyzers flag resources opened and not closed on every path.
- Watch open file descriptor counts, connection pool use and memory in production. A steady climb under constant load is the signature.
- Run load tests for a long duration, not only a short burst.
- Use leak detectors during testing, such as AddressSanitizer or Valgrind for native code.
- Review every early return and exception path in functions that acquire resources.
How to fix it
The record lists no formal mitigations, so these are standard practice.
- Use the language's scoped cleanup construct:
within Python, try with resources in Java,usingin C#,deferin Go, RAII in C++. - Avoid relying on finalizers for anything time sensitive. They run when the runtime decides, not when you need them.
- Give every resource a single clear owner.
- In C++, prefer smart pointers and containers over raw
newanddelete. - Add tests that call the failure paths, not only the success path, and assert that resources are released.
- Set limits and timeouts on pools so a leak shows up as a clear error rather than a slow hang.
Try it yourself
See how a static scan surfaces code level issues in a sample project.
Click through it at your own pace, or open it full screen.
Quick check
Why is relying on garbage collection not enough?
Collection timing is not predictable, and file handles, sockets and connections are limited. They need to be released at a known point, not at some later time.
Which code paths do leaks usually hide in?
Early returns and exception handlers. The happy path usually cleans up, and the error path does not.