Weakness reference
CWE-772

Missing Release of Resource after Effective Lifetime

This weakness occurs when a program acquires a resource—such as a file handle, database connection, network socket, or memory allocation—but fails to release…

01Summary

This weakness occurs when a program acquires a resource—such as a file handle, database connection, network socket, or memory allocation—but fails to release it after use. Over time, repeated resource leaks exhaust the system's available resources, causing the application to slow down, crash, or become unresponsive. Unlike memory leaks alone, this covers any managed resource that has a finite pool.

02How It Happens

Resources like file handles and database connections are typically limited by the operating system or application server. When code opens a resource but neglects to close it—either through missing cleanup code, exception handling that bypasses cleanup, or logic errors—that resource remains allocated. If this happens repeatedly in a loop or on every request, the pool of available resources shrinks until none remain. New requests then fail or hang waiting for a resource to become available.

The root cause is usually one of three patterns: no explicit cleanup call at all, cleanup code that is skipped due to an early return or exception, or cleanup code placed in a code path that is never reached under certain conditions.

03Real-World Impact

Resource exhaustion can render an application unusable. A web application that leaks a database connection on every request will eventually exhaust the connection pool, causing all subsequent requests to fail or timeout. Similarly, a service that leaks file handles may hit the operating system's per-process file descriptor limit, preventing the application from opening new files or accepting network connections. In shared hosting or containerized environments, resource exhaustion can also affect other applications on the same system.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import sqlite3

def fetch_user_data(user_id):
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
    result = cursor.fetchone()
    return result

Why it's vulnerable:
The database connection is never closed. If this function is called repeatedly (e.g., on every HTTP request), the connection pool will eventually be exhausted and new requests will fail.

Fixed pattern
import sqlite3

def fetch_user_data(user_id):
    conn = sqlite3.connect('app.db')
    try:
        cursor = conn.cursor()
        cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
        result = cursor.fetchone()
        return result
    finally:
        conn.close()
Vulnerable pattern
<?php
function read_config_file($filename) {
    $handle = fopen($filename, 'r');
    if (!$handle) {
        return null;
    }
    $content = fread($handle, filesize($filename));
    return $content;
}
?>

Why it's vulnerable:
The file handle is opened but never closed with fclose(). If this function is called many times, the process will eventually hit the file descriptor limit and fail to open new files.

Fixed pattern
<?php
function read_config_file($filename) {
    $handle = fopen($filename, 'r');
    if (!$handle) {
        return null;
    }
    try {
        $content = fread($handle, filesize($filename));
        return $content;
    } finally {
        fclose($handle);
    }
}
?>

05Prevention Checklist

Use context managers or try-finally blocks
— Ensure cleanup code runs regardless of how the function exits (normal return, exception, or early return).
Audit resource-opening calls
— Search your codebase for open(), fopen(), connect(), socket(), and similar functions; verify each has a corresponding close/release call.
Test under load
— Run integration tests that repeatedly perform resource-acquiring operations and monitor system resource usage (file descriptors, connection pool size) to catch leaks early.
Set resource limits and monitor them
— Configure appropriate limits on file descriptors, database connections, and memory; log warnings when approaching limits.
Use connection pooling and timeouts
— For database and network resources, employ pooling libraries that manage lifecycle automatically and set idle timeouts to reclaim stale connections.
Code review resource-heavy paths
— Pay special attention to loops, request handlers, and exception paths where resources are acquired.

06Signs You May Already Be Affected

Monitor your application logs and system metrics for patterns such as: gradual increase in response times or timeouts over hours or days (especially after restart, performance returns briefly), "too many open files" or "connection pool exhausted" errors in logs, or a rising count of file descriptors or database connections shown by system tools like lsof or database admin panels. These are strong indicators that resources are not being released properly.

07Related Recent Vulnerabilities