Skip to content

CWE-404: Improper Resource Shutdown or Release

Overview

Improper resource shutdown occurs when applications fail to close files, database connections, sockets, or other resources after use, leading to resource exhaustion, file descriptor leaks, connection pool depletion, and denial of service.

Risk

Medium-High: Missing resource cleanup causes connection pool exhaustion, file descriptor limits hit, memory leaks, degraded performance, denial of service, locked files, and eventual application crashes. Can expose sensitive data in open connections.

Remediation Steps

Core principle: Ensure cleanup occurs on all paths (including errors); secure failure includes resource and state cleanup.

Locate the Resource Leak Vulnerability

When reviewing security scan results:

  • Examine data_paths: Identify where resources are acquired but not properly released
  • Identify resource types: Files, database connections, sockets, streams, handles
  • Trace resource lifecycle: Look for missing close/dispose calls in all code paths
  • Check exception paths: Ensure resources are closed even when exceptions occur
  • Review control flow: Identify branches where close() calls might not be reached

Use Automatic Resource Management (Primary Defense)

Java (try-with-resources):

try (Connection conn = getConnection();
     Statement stmt = conn.createStatement()) {
    stmt.execute(sql);
}  // Auto-closed

Python (with statement):

with open(file, 'r') as f:
    data = f.read()

# Auto-closed

C# (using statement):

using (var stream = new FileStream(path, FileMode.Open)) {
    // Use stream
}  // Auto-disposed

Why this works: Language-specific automatic resource management ensures cleanup occurs even during exceptions, prevents forgetting to close resources, and handles complex cleanup scenarios correctly.

Ensure Finally Blocks Execute Properly

For languages without automatic resource management:

InputStream is = null;
try {
    is = new FileInputStream(file);
    process(is);
} finally {
    if (is != null) {
        try { is.close(); } catch (IOException e) { }
    }
}

Implementation details:

  • Always use finally blocks for resource cleanup when try-with-resources is unavailable
  • Check for null before closing to avoid NullPointerException
  • Catch and suppress close() exceptions to avoid masking original exceptions
  • Place all cleanup code in finally to ensure execution

Close Resources in Correct Order

Ordering rules:

  • Close in reverse order of opening (LIFO - Last In, First Out)
  • Close ResultSet before Statement before Connection
  • Close derived streams before base streams
  • Close streams before sockets
  • Close child resources before parent resources

Example:

try (Connection conn = getConnection();
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
    // Resources closed in reverse order: rs, stmt, conn
}

Handle Cleanup Exceptions Appropriately

Exception handling strategies:

  • Don't let cleanup exceptions mask the original exception
  • Log cleanup failures for debugging
  • Use try-with-resources which automatically handles suppressed exceptions
  • Use addSuppressed() to attach cleanup exceptions to primary exception

Example:

Exception primary = null;
try {
    riskyOperation();
} catch (Exception e) {
    primary = e;
} finally {
    try {
        cleanup();
    } catch (Exception e) {
        if (primary != null) {
            primary.addSuppressed(e);
        }
    }
    if (primary != null) throw primary;
}

Monitor and Test for Resource Leaks

Testing approaches:

  • Monitor file descriptor usage during testing
  • Use memory profilers to detect resource leaks
  • Test exception paths explicitly
  • Run load tests to expose connection pool exhaustion
  • Check metrics: open files, connections, handles

Verification steps:

  • Run application under normal and error conditions
  • Monitor resource usage over extended periods
  • Use tools like lsof (Linux) or Resource Monitor (Windows)
  • Set up alerts for resource threshold violations

Common Vulnerable Patterns

// No close
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
// Missing reader.close()

// Close only on success
Connection conn = getConnection();
if (condition) {
    conn.close();  // Not always reached
}

// Exception prevents close
FileInputStream fis = new FileInputStream(file);
process(fis);  // Might throw
fis.close();   // Never reached if exception

Additional Resources