Multiple Releases of Same Resource or Handle

Description

Multiple Releases of Same Resource or Handle occurs when a product attempts to close or release a resource or handle more than once, without any successful open between the close operations. Resources like memory, files, devices, and socket connections require "opening" and subsequent "closing" or "releasing." When developers call release APIs (such as free() in C/C++ or close() operations) more than once on the same resource, the API's expectations are violated, leading to undefined or insecure behavior including memory corruption, data corruption, or execution path corruption. The reuse of resource identifiers can result in closing the wrong resource.

Risk

Multiple releases have severe implications. Memory corruption from double-free. Use-after-free vulnerabilities. Application crashes. Arbitrary code execution through heap manipulation. Data corruption. Resource exhaustion. Security bypass through corrupted state. Wrong resource closure. High likelihood in complex error handling paths.

Solution

Simplify logic to ensure resources close only once through refactoring during implementation phase. Use flags to track resource state—set when opened, clear when closed, check before closing. Set resource variables to NULL after closing since some APIs ignore null values without errors. Use RAII patterns in C++ to automatically manage resource lifetimes. Implement resource wrapper classes that track state.

Common Consequences

ImpactDetails
AvailabilityScope: Availability, Integrity

Program faults, crashes, or unpredictable behavior from resource management errors.
IntegrityScope: Integrity

Memory or data corruption from invalid resource state.

Example Code

Vulnerable Code

// Vulnerable: C code with double close/free

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// VULNERABLE: Double fclose
void vulnerable_file_handling(const char* filename) {
    char buffer[1024];
    FILE *f = fopen(filename, "r");

    if (f) {
        fread(buffer, 1, sizeof(buffer) - 1, f);

        int r1 = fclose(f);  // First close - OK

        // ... some processing ...

        int r2 = fclose(f);  // VULNERABLE: Double close
        // Undefined behavior - may crash or corrupt memory
    }
}

// VULNERABLE: Double free
void vulnerable_memory_handling(size_t size) {
    char* ptr = (char*)malloc(size);

    if (ptr == NULL) {
        return;
    }

    // ... use ptr ...

    if (some_error_condition) {
        free(ptr);  // First free in error path
    }

    // ... more processing ...

    free(ptr);  // VULNERABLE: Second free - may already be freed
    // Heap corruption, potential code execution
}

// VULNERABLE: Double close in error handling
int vulnerable_socket_handling(int port) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    if (sock < 0) {
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        close(sock);  // Close on bind failure
        return -1;
    }

    if (listen(sock, 5) < 0) {
        close(sock);  // Close on listen failure
        return -1;
    }

    // ... accept connections ...

    close(sock);  // Normal close

    // Later in cleanup code
    close(sock);  // VULNERABLE: Double close if error occurred

    return 0;
}

// VULNERABLE: Resource handle reuse confusion
void vulnerable_handle_reuse() {
    int fd1 = open("file1.txt", O_RDONLY);
    int fd2 = open("file2.txt", O_RDONLY);

    // ... operations ...

    close(fd1);  // Close fd1

    // fd1's number might be reused for new file
    int fd3 = open("file3.txt", O_RDONLY);  // May get same fd as fd1

    // Mistakenly close what we think is fd1 again
    close(fd1);  // VULNERABLE: Actually closes fd3!
}
// Vulnerable: Java resource handling

import java.io.*;
import java.sql.*;

public class VulnerableResources {

    // VULNERABLE: Double close on stream
    public void vulnerableStreamHandling(String filename) {
        FileInputStream fis = null;
        BufferedInputStream bis = null;

        try {
            fis = new FileInputStream(filename);
            bis = new BufferedInputStream(fis);

            // ... read data ...

            bis.close();  // Closes both bis and fis
            fis.close();  // VULNERABLE: fis already closed by bis

        } catch (IOException e) {
            try {
                if (bis != null) bis.close();  // May throw if already closed
                if (fis != null) fis.close();  // Double close
            } catch (IOException ex) {
                // Suppressed
            }
        }
    }

    // VULNERABLE: Database connection double close
    public void vulnerableDbHandling() throws SQLException {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = DriverManager.getConnection("jdbc:...");
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT * FROM users");

            // ... process results ...

        } finally {
            // VULNERABLE: May close multiple times on exception
            if (rs != null) rs.close();
            if (stmt != null) stmt.close();  // Also closes rs
            if (conn != null) conn.close();  // Also closes stmt

            // Later cleanup attempt
            if (rs != null) rs.close();  // VULNERABLE: Double close
        }
    }
}
# Vulnerable: Python resource handling

# VULNERABLE: Double close on file
def vulnerable_file_handling(filename):
    f = open(filename, 'r')
    try:
        data = f.read()
        f.close()  # First close

        # ... process data ...

    finally:
        f.close()  # VULNERABLE: Double close
        # May raise ValueError: I/O operation on closed file

# VULNERABLE: Thread-unsafe resource release
import threading

class VulnerableResource:
    def __init__(self):
        self.handle = allocate_resource()
        self.released = False

    def release(self):
        # VULNERABLE: Race condition allows double release
        if not self.released:
            # Thread A and B both pass this check
            free_resource(self.handle)  # Both threads free
            self.released = True

# VULNERABLE: Exception handling double release
def vulnerable_multi_resource():
    res1 = None
    res2 = None

    try:
        res1 = acquire_resource1()
        res2 = acquire_resource2()  # May throw

        # ... use resources ...

    except Exception as e:
        if res1:
            release_resource(res1)  # Release in exception
        if res2:
            release_resource(res2)
        raise
    finally:
        # VULNERABLE: Also releases in finally
        if res1:
            release_resource(res1)  # Double release if exception
        if res2:
            release_resource(res2)

Fixed Code

// Fixed: Safe resource handling in C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// FIXED: Single close with NULL check
void safe_file_handling(const char* filename) {
    char buffer[1024];
    FILE *f = fopen(filename, "r");

    if (f) {
        fread(buffer, 1, sizeof(buffer) - 1, f);

        int r = fclose(f);
        f = NULL;  // FIXED: Set to NULL after close

        // ... some processing ...

        // FIXED: Check before close
        if (f != NULL) {
            fclose(f);
            f = NULL;
        }
    }
}

// FIXED: Memory handling with NULL pattern
void safe_memory_handling(size_t size) {
    char* ptr = (char*)malloc(size);

    if (ptr == NULL) {
        return;
    }

    // ... use ptr ...

    if (some_error_condition) {
        free(ptr);
        ptr = NULL;  // FIXED: Set to NULL after free
    }

    // ... more processing ...

    // FIXED: Check before free
    if (ptr != NULL) {
        free(ptr);
        ptr = NULL;
    }
}

// FIXED: Using flag-based tracking
typedef struct {
    FILE* file;
    int is_open;
} safe_file_t;

void safe_file_open(safe_file_t* sf, const char* filename, const char* mode) {
    sf->file = fopen(filename, mode);
    sf->is_open = (sf->file != NULL) ? 1 : 0;
}

void safe_file_close(safe_file_t* sf) {
    // FIXED: Check flag before closing
    if (sf->is_open && sf->file != NULL) {
        fclose(sf->file);
        sf->file = NULL;
        sf->is_open = 0;
    }
}

// FIXED: Socket handling with single close point
int safe_socket_handling(int port) {
    int sock = -1;
    int result = -1;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        goto cleanup;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        goto cleanup;
    }

    if (listen(sock, 5) < 0) {
        goto cleanup;
    }

    // ... accept connections ...

    result = 0;

cleanup:
    // FIXED: Single close point
    if (sock >= 0) {
        close(sock);
        sock = -1;
    }

    return result;
}

// FIXED: RAII-style wrapper in C
typedef struct {
    int fd;
    int valid;
} safe_fd_t;

safe_fd_t safe_fd_open(const char* path, int flags) {
    safe_fd_t sfd;
    sfd.fd = open(path, flags);
    sfd.valid = (sfd.fd >= 0);
    return sfd;
}

void safe_fd_close(safe_fd_t* sfd) {
    if (sfd->valid && sfd->fd >= 0) {
        close(sfd->fd);
        sfd->fd = -1;
        sfd->valid = 0;
    }
}
// Fixed: Safe resource handling in Java

import java.io.*;
import java.sql.*;

public class SafeResources {

    // FIXED: Use try-with-resources
    public void safeStreamHandling(String filename) {
        // FIXED: Automatic resource management
        try (FileInputStream fis = new FileInputStream(filename);
             BufferedInputStream bis = new BufferedInputStream(fis)) {

            // ... read data ...

        } catch (IOException e) {
            // Handle exception - resources auto-closed
            e.printStackTrace();
        }
        // No manual close needed - guaranteed single close
    }

    // FIXED: Database connection with try-with-resources
    public void safeDbHandling() {
        // FIXED: Automatic resource management
        try (Connection conn = DriverManager.getConnection("jdbc:...");
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {

            // ... process results ...

        } catch (SQLException e) {
            e.printStackTrace();
        }
        // Resources closed in reverse order, exactly once
    }

    // FIXED: Custom AutoCloseable with state tracking
    public class SafeResource implements AutoCloseable {
        private Object handle;
        private boolean closed = false;

        public SafeResource() {
            this.handle = acquireResource();
        }

        @Override
        public synchronized void close() {
            // FIXED: Thread-safe single close
            if (!closed && handle != null) {
                releaseResource(handle);
                handle = null;
                closed = true;
            }
        }

        public boolean isClosed() {
            return closed;
        }
    }
}
# Fixed: Safe resource handling in Python

# FIXED: Using context manager
def safe_file_handling(filename):
    # FIXED: Context manager ensures single close
    with open(filename, 'r') as f:
        data = f.read()
        # ... process data ...
    # File automatically closed exactly once

# FIXED: Thread-safe resource release
import threading

class SafeResource:
    def __init__(self):
        self.handle = allocate_resource()
        self.released = False
        self._lock = threading.Lock()

    def release(self):
        # FIXED: Thread-safe with lock
        with self._lock:
            if not self.released:
                free_resource(self.handle)
                self.handle = None
                self.released = True

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()
        return False

# FIXED: Using context manager for multiple resources
from contextlib import ExitStack

def safe_multi_resource():
    # FIXED: ExitStack handles multiple resources safely
    with ExitStack() as stack:
        res1 = stack.enter_context(acquire_resource1())
        res2 = stack.enter_context(acquire_resource2())

        # ... use resources ...

    # Both resources closed exactly once, even on exception

# FIXED: Custom context manager with state
class ManagedResource:
    def __init__(self):
        self._handle = None
        self._acquired = False

    def __enter__(self):
        self._handle = acquire_raw_resource()
        self._acquired = True
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # FIXED: Only release if acquired
        if self._acquired and self._handle is not None:
            release_raw_resource(self._handle)
            self._handle = None
            self._acquired = False
        return False

# Usage
def safe_operation():
    with ManagedResource() as res:
        # ... use res ...
        pass
    # Guaranteed single release

CVE Examples

  • CVE-2019-5018: SQLite3 window function double-free vulnerability enabling code execution.
  • CVE-2018-14618: curl double-free in NTLM authentication code.
  • CVE-2017-9047: libxml2 double-free in XML validation.

  • CWE-675: Multiple Operations on Resource in Single-Operation Context (parent)
  • CWE-415: Double Free (child)
  • CWE-672: Operation on a Resource after Expiration or Release (can follow)
  • CWE-399: Resource Management Errors (category)

References

  1. MITRE Corporation. "CWE-1341: Multiple Releases of Same Resource or Handle." https://cwe.mitre.org/data/definitions/1341.html
  2. CERT C. "MEM30-C. Do not access freed memory"
  3. SEI CERT. "Double-Free Vulnerabilities"