Missing Release of Resource after Effective Lifetime
Description
Missing Release of Resource after Effective Lifetime is a resource management vulnerability where software fails to release a resource after it is no longer needed. This applies to various resource types including memory allocations, file handles, database connections, network sockets, locks, and other system resources. When resources are not properly released, they remain allocated indefinitely, gradually consuming available resources until exhaustion occurs. This weakness affects virtually all programming languages and platforms, though languages with automatic garbage collection may handle some resource types automatically.
Risk
Failure to release resources leads to resource leaks that accumulate over time, eventually causing resource exhaustion. This can result in denial of service as the application or system runs out of available resources. Memory leaks cause applications to consume increasing amounts of memory until they crash or trigger out-of-memory conditions. File descriptor leaks prevent opening new files or network connections. Database connection leaks exhaust connection pools. Attackers can accelerate resource exhaustion by repeatedly triggering code paths that allocate without releasing. In long-running services, even small leaks become critical over time.
Solution
Ensure every allocated resource is released when no longer needed. Use RAII (Resource Acquisition Is Initialization) patterns in C++ where destructors automatically release resources. In Java and similar languages, use try-with-resources or try-finally patterns to guarantee cleanup. Close files, sockets, and connections explicitly at all exit points including error paths. Use garbage-collected languages where appropriate, but remember that GC handles memory only—other resources still need explicit release. Implement resource pools with automatic reclamation. Use OS-level resource limits (setrlimit) to prevent complete exhaustion. Monitor resource usage and implement alerts for unusual consumption patterns.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Availability DoS: Resource Consumption (Memory) - Memory leaks exhaust available memory, causing crashes or system-wide impact. |
| Availability | Scope: Availability DoS: Resource Consumption (Other) - File descriptor, socket, or connection leaks prevent new allocations. |
| Availability | Scope: Availability DoS: Resource Consumption (CPU) - Resource tracking overhead increases as leaks accumulate. |
Example Code
Vulnerable Code
// Vulnerable: File handle never explicitly closed
private void processFile(String fName) throws IOException {
BufferedReader fil = new BufferedReader(new FileReader(fName));
String line;
while ((line = fil.readLine()) != null) {
processLine(line);
}
// Vulnerable: fil.close() never called
// File descriptor leaked
}
// Vulnerable: Exception prevents close
private void processFileWithException(String fName) throws IOException {
BufferedReader fil = new BufferedReader(new FileReader(fName));
String line;
while ((line = fil.readLine()) != null) {
processLine(line); // May throw exception
}
fil.close(); // Never reached if exception thrown
}
// Vulnerable: File handle not closed on error path
int decodeFile(char* fName) {
int rc;
FILE* f = fopen(fName, "r");
if (!f) {
return DECODE_FAIL;
}
rc = setRecordType(f);
if (rc != SUCCESS) {
return DECODE_FAIL; // Vulnerable: f not closed!
}
rc = processRecords(f);
if (rc != SUCCESS) {
return DECODE_FAIL; // Vulnerable: f not closed!
}
fclose(f);
return DECODE_SUCCESS;
}
// Vulnerable: Socket not closed
int connectToServer(const char* host, int port) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return -1;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
return -1; // Vulnerable: sockfd leaked!
}
return sockfd;
}
# Vulnerable: Database connection not closed
def query_database(query):
conn = psycopg2.connect(database="mydb")
cursor = conn.cursor()
cursor.execute(query)
results = cursor.fetchall()
# Vulnerable: connection and cursor never closed
return results
# Vulnerable: File not closed on exception
def read_config(filename):
f = open(filename, 'r')
config = json.load(f) # May raise exception
f.close() # Never reached if exception
return config
// Vulnerable: Memory allocated but not freed
void processData(int size) {
char* buffer = new char[size];
if (!validateSize(size)) {
return; // Vulnerable: buffer leaked
}
process(buffer);
delete[] buffer;
}
// Vulnerable: Multiple resources, one leaked
void handleRequest() {
FILE* inputFile = fopen("input.txt", "r");
FILE* outputFile = fopen("output.txt", "w");
if (inputFile == NULL || outputFile == NULL) {
// Vulnerable: If one succeeded, it's leaked
return;
}
// ... process ...
fclose(inputFile);
fclose(outputFile);
}
Fixed Code
// Fixed: Using try-with-resources (Java 7+)
private void processFile(String fName) throws IOException {
try (BufferedReader fil = new BufferedReader(new FileReader(fName))) {
String line;
while ((line = fil.readLine()) != null) {
processLine(line);
}
} // Automatically closed, even on exception
}
// Fixed: Using try-finally (older Java)
private void processFileLegacy(String fName) throws IOException {
BufferedReader fil = null;
try {
fil = new BufferedReader(new FileReader(fName));
String line;
while ((line = fil.readLine()) != null) {
processLine(line);
}
} finally {
if (fil != null) {
fil.close();
}
}
}
// Fixed: Close file on all paths
int decodeFile(char* fName) {
int rc;
int result = DECODE_SUCCESS;
FILE* f = fopen(fName, "r");
if (!f) {
return DECODE_FAIL;
}
rc = setRecordType(f);
if (rc != SUCCESS) {
result = DECODE_FAIL;
goto cleanup;
}
rc = processRecords(f);
if (rc != SUCCESS) {
result = DECODE_FAIL;
goto cleanup;
}
cleanup:
fclose(f); // Always close
return result;
}
// Fixed: Close socket on connect failure
int connectToServer(const char* host, int port) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return -1;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(sockfd); // Fixed: Close on failure
return -1;
}
return sockfd;
}
# Fixed: Using context manager
def query_database(query):
with psycopg2.connect(database="mydb") as conn:
with conn.cursor() as cursor:
cursor.execute(query)
results = cursor.fetchall()
return results # Connection automatically closed
# Fixed: File with context manager
def read_config(filename):
with open(filename, 'r') as f:
config = json.load(f) # File closed even on exception
return config
# Fixed: Using try-finally for resources without context manager
def process_resource():
resource = acquire_resource()
try:
do_work(resource)
finally:
release_resource(resource)
// Fixed: Using RAII with smart pointer or wrapper
#include <memory>
#include <fstream>
void processData(int size) {
std::unique_ptr<char[]> buffer(new char[size]);
// Or: auto buffer = std::make_unique<char[]>(size);
if (!validateSize(size)) {
return; // buffer automatically freed
}
process(buffer.get());
// buffer automatically freed when going out of scope
}
// Fixed: RAII wrapper for FILE*
class FileHandle {
FILE* file;
public:
FileHandle(const char* path, const char* mode)
: file(fopen(path, mode)) {}
~FileHandle() {
if (file) fclose(file);
}
FILE* get() { return file; }
bool isValid() { return file != nullptr; }
// Prevent copying
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
};
void handleRequest() {
FileHandle inputFile("input.txt", "r");
FileHandle outputFile("output.txt", "w");
if (!inputFile.isValid() || !outputFile.isValid()) {
return; // Both automatically closed
}
// ... process ...
} // Both automatically closed
CVE Examples
- CVE-2007-0897: Anti-virus product failed to close file descriptor when encountering malformed files, leading to file descriptor exhaustion and failed scans.
- CVE-2001-0830: Sockets remained open during repeated connect/disconnect cycles, exhausting available sockets.
- CVE-2009-2054: File descriptor exhaustion from processing large TCP packet volumes.
References
- MITRE Corporation. "CWE-772: Missing Release of Resource after Effective Lifetime." https://cwe.mitre.org/data/definitions/772.html
- CERT C Coding Standard. "FIO42-C. Close files when they are no longer needed."
- C++ Core Guidelines. "R.1: Manage resources automatically using resource handles and RAII."