Weakness reference
CWE-404

Improper Resource Shutdown or Release

This weakness occurs when software fails to properly close or release resources—such as file handles, database connections, network sockets, or locks—after…

01Summary

This weakness occurs when software fails to properly close or release resources—such as file handles, database connections, network sockets, or locks—after they are no longer needed. Over time, unreleased resources accumulate and exhaust system limits, causing the application to crash, become unresponsive, or behave unpredictably. This is a common but often overlooked vulnerability in long-running services and applications that handle many concurrent operations.

02How It Happens

Resources in modern applications are finite. When code opens a file, establishes a database connection, or acquires a lock, the operating system or runtime allocates memory and system structures to track that resource. If the code path that should close or release that resource is skipped—due to an early return, an unhandled exception, or simply forgotten cleanup—the resource remains allocated. In single-use scripts this may go unnoticed, but in servers handling thousands of requests, each leaked resource compounds. Eventually, the system runs out of available file descriptors, connection slots, or memory, and new operations fail.

03Real-World Impact

Resource exhaustion can render an application unusable. A web server that leaks database connections may eventually refuse new requests because all connection slots are occupied by abandoned connections. A service that doesn't close file handles may hit the operating system's file descriptor limit and crash. In multi-tenant or shared hosting environments, resource leaks in one application can degrade performance for others. The impact is often gradual—the application works fine initially, then mysteriously slows down or fails after hours or days of operation, making the root cause hard to diagnose.

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,))
    row = cursor.fetchone()
    if row is None:
        return None  # Connection and cursor never closed
    return row

Why it's vulnerable:
The database connection and cursor are never explicitly closed. If this function is called repeatedly (e.g., in a web request handler), connections accumulate and eventually exhaust the database's connection limit.

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,))
        row = cursor.fetchone()
        return row
    finally:
        conn.close()
Vulnerable pattern
<?php
function read_config_file($filename) {
    $handle = fopen($filename, 'r');
    if (!$handle) {
        return false;  // File handle never closed
    }
    $data = fread($handle, filesize($filename));
    if (strlen($data) === 0) {
        return null;  // Early return, handle not closed
    }
    return $data;
}
?>

Why it's vulnerable:
The file handle opened by fopen() is not closed in either the error case or the early return case. Repeated calls leak file descriptors.

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

05Prevention Checklist

Use language-native resource management:
Leverage with statements (Python), try/finally blocks, or defer patterns to guarantee cleanup even if exceptions occur.
Audit all resource-opening calls:
Search your codebase for open(), fopen(), connect(), socket(), lock(), and similar functions; verify each has a corresponding close/release in all code paths.
Monitor resource usage in production:
Track open file descriptors, database connections, and memory usage over time; sudden growth indicates a leak.
Set resource limits and timeouts:
Configure OS-level limits (e.g., ulimit) and application-level connection timeouts so leaks fail fast rather than silently accumulating.
Test under load:
Run integration tests that simulate sustained traffic; resource leaks often only surface after hundreds or thousands of operations.
Use connection pooling and object pooling:
For databases and sockets, use pooling libraries that manage lifecycle automatically and prevent accidental leaks.

06Signs You May Already Be Affected

Monitor your application logs and system metrics for gradual degradation: increasing response times, "too many open files" errors, "connection pool exhausted" messages, or out-of-memory crashes after the application has been running for hours or days. Check your operating system's resource usage (lsof on Linux, Resource Monitor on Windows) to see if your application process is holding an unusually high number of open file descriptors or network connections compared to the number of active requests.

07Related Recent Vulnerabilities