Operation on a Resource after Expiration or Release

Description

Operation on a Resource after Expiration or Release occurs when code continues to use a resource after it has been released, expired, or otherwise invalidated. This includes using memory after it's been freed (use-after-free), accessing files after they've been closed, using network connections after disconnection, operating on expired credentials or tokens, and referencing objects after their lifetime has ended. Such operations lead to undefined behavior and security vulnerabilities.

Risk

Use-after-free enables arbitrary code execution. Expired tokens allow continued access. Closed connections leak data or cause crashes. Stale credentials bypass revocation. Released resources may be reallocated and misused. Memory corruption from dangling pointers. Data integrity issues from expired cache entries.

Solution

Nullify pointers after freeing memory. Track resource state explicitly. Use smart pointers and RAII patterns. Implement proper token expiration checking. Validate resource availability before use. Use language features that prevent use-after-free. Implement proper cleanup sequences.

Common Consequences

ImpactDetails
IntegrityScope: Memory Corruption

Use-after-free corrupts memory state.
ConfidentialityScope: Information Disclosure

Released memory may contain sensitive data.
AuthorizationScope: Access Control Bypass

Expired credentials still grant access.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Use-after-free and related issues
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// VULNERABLE: Classic use-after-free
void use_after_free_vulnerable() {
    char* buffer = malloc(100);
    strcpy(buffer, "sensitive data");

    free(buffer);

    // VULNERABLE: Using freed memory
    printf("Data: %s\n", buffer);  // Use-after-free
    strcpy(buffer, "new data");     // Write to freed memory
}

// VULNERABLE: Dangling pointer in struct
typedef struct {
    char* name;
    int id;
} User;

void dangling_pointer_vulnerable(User* user) {
    free(user->name);
    // VULNERABLE: user->name is now dangling
    // Other code may still access it
}

// VULNERABLE: Double-free with reuse
void double_free_reuse_vulnerable() {
    char* ptr = malloc(64);
    free(ptr);

    // Memory may be reallocated
    char* ptr2 = malloc(64);  // May get same address

    free(ptr);  // VULNERABLE: Frees ptr2's memory!
    strcpy(ptr2, "data");  // Use-after-free
}

// VULNERABLE: Iterator invalidation
void iterator_vulnerable() {
    int* array = malloc(10 * sizeof(int));
    int* ptr = array + 5;

    // Realloc may move memory
    array = realloc(array, 20 * sizeof(int));

    // VULNERABLE: ptr may now be dangling
    *ptr = 42;  // May crash or corrupt memory
}

// VULNERABLE: Return pointer to local/freed
char* return_freed_vulnerable() {
    char* buffer = malloc(100);
    strcpy(buffer, "data");
    free(buffer);
    return buffer;  // VULNERABLE: Returns freed pointer
}
# VULNERABLE: Python resource expiration issues
import weakref
from datetime import datetime, timedelta

# VULNERABLE: Using closed file
def file_after_close_vulnerable():
    f = open('data.txt', 'r')
    content = f.read()
    f.close()

    # VULNERABLE: File is closed
    f.seek(0)  # ValueError: I/O operation on closed file
    more_content = f.read()

# VULNERABLE: Database connection after release
class VulnerableConnectionPool:
    def get_connection(self):
        conn = self.pool.pop()
        return conn

    def release(self, conn):
        conn.close()
        # Connection is closed but reference exists

def use_connection_vulnerable(pool):
    conn = pool.get_connection()
    cursor = conn.cursor()

    pool.release(conn)  # Connection closed

    # VULNERABLE: Using closed connection
    cursor.execute("SELECT * FROM users")  # Error

# VULNERABLE: Expired token still used
class VulnerableTokenAuth:
    def __init__(self):
        self.token = None
        self.expires_at = None

    def authenticate(self, token):
        self.token = token
        self.expires_at = datetime.now() + timedelta(hours=1)

    def make_request(self, endpoint):
        # VULNERABLE: Doesn't check expiration
        headers = {'Authorization': f'Bearer {self.token}'}
        return requests.get(endpoint, headers=headers)

# VULNERABLE: Iterator after modification
def iterator_vulnerable():
    items = [1, 2, 3, 4, 5]
    for item in items:
        if item == 3:
            items.remove(item)  # VULNERABLE: Modifying during iteration
        print(item)  # May skip elements
// VULNERABLE: Java resource expiration issues
import java.sql.*;
import java.util.*;

public class VulnerableResourceExpiration {

    // VULNERABLE: Connection used after close
    public void connectionAfterClose() throws SQLException {
        Connection conn = DriverManager.getConnection(url);
        Statement stmt = conn.createStatement();

        conn.close();

        // VULNERABLE: Statement invalid after connection close
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    }

    // VULNERABLE: Using expired iterator
    public void iteratorAfterModification() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
        Iterator<String> iter = list.iterator();

        list.add("d");  // Modifies list

        // VULNERABLE: ConcurrentModificationException
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }
    }

    // VULNERABLE: Closed stream usage
    public void streamAfterClose() throws IOException {
        InputStream stream = new FileInputStream("data.txt");
        stream.close();

        // VULNERABLE: Stream is closed
        int data = stream.read();  // IOException
    }

    // VULNERABLE: Expired session token
    private Map<String, Session> sessions = new HashMap<>();

    public Object getData(String sessionId) {
        Session session = sessions.get(sessionId);

        // VULNERABLE: Doesn't check if session expired
        return session.getData();  // May return data for expired session
    }

    // VULNERABLE: Reference after clear
    private List<User> cachedUsers = new ArrayList<>();

    public void clearAndUse() {
        User user = cachedUsers.get(0);  // Get reference
        cachedUsers.clear();  // Clear list

        // Reference still exists but context is gone
        // VULNERABLE: user.group may reference cleared data
        processUser(user);
    }
}
// VULNERABLE: JavaScript resource expiration issues
class VulnerableResources {

    // VULNERABLE: WebSocket after close
    useClosedWebSocket() {
        const ws = new WebSocket('ws://example.com');

        ws.onopen = () => {
            ws.send('Hello');
        };

        setTimeout(() => {
            ws.close();
        }, 1000);

        setTimeout(() => {
            // VULNERABLE: WebSocket is closed
            ws.send('Data');  // Error: WebSocket is already closed
        }, 2000);
    }

    // VULNERABLE: Expired promise reference
    async expiredPromise() {
        let data = null;
        const promise = fetch('/api/data').then(r => r.json());

        setTimeout(async () => {
            // VULNERABLE: Promise may have resolved/rejected
            // and data may be stale or context changed
            data = await promise;
            this.processData(data);  // Using potentially stale data
        }, 60000);
    }

    // VULNERABLE: Expired JWT token
    makeAuthenticatedRequest() {
        const token = localStorage.getItem('jwt');

        // VULNERABLE: No expiration check
        return fetch('/api/secure', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
    }

    // VULNERABLE: Timer callback after component unmount
    componentDidMount() {
        this.timer = setInterval(() => {
            // VULNERABLE: May run after unmount
            this.setState({ count: this.state.count + 1 });
        }, 1000);
    }

    // VULNERABLE: Event listener after element removal
    setupListener() {
        const button = document.getElementById('myButton');
        const handler = () => {
            console.log('Clicked');
        };

        button.addEventListener('click', handler);

        // Later: button is removed from DOM
        button.remove();

        // VULNERABLE: Handler still exists but element is gone
        // Memory leak and potential issues
    }
}

Fixed Code

// SAFE: Proper resource lifecycle management
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// SAFE: Nullify after free
void use_after_free_safe() {
    char* buffer = malloc(100);
    if (!buffer) return;

    strcpy(buffer, "sensitive data");

    // Clear sensitive data before free
    memset(buffer, 0, 100);
    free(buffer);
    buffer = NULL;  // SAFE: Prevent use-after-free

    // Safe: NULL check prevents issues
    if (buffer) {
        printf("Data: %s\n", buffer);
    }
}

// SAFE: Structured cleanup
typedef struct {
    char* name;
    int id;
    int name_valid;  // Track if name is valid
} SafeUser;

void cleanup_user_safe(SafeUser* user) {
    if (user && user->name && user->name_valid) {
        free(user->name);
        user->name = NULL;
        user->name_valid = 0;
    }
}

// SAFE: Single ownership pattern
void single_ownership_safe() {
    char* ptr = malloc(64);
    if (!ptr) return;

    // Transfer ownership explicitly
    process_and_free(ptr);  // This function takes ownership
    ptr = NULL;  // We no longer own it

    // No double-free possible
}

// SAFE: Handle realloc properly
void realloc_safe() {
    int* array = malloc(10 * sizeof(int));
    if (!array) return;

    int* new_array = realloc(array, 20 * sizeof(int));
    if (new_array) {
        // SAFE: Update all pointers
        array = new_array;
    } else {
        // Handle error, old array still valid
        free(array);
        return;
    }

    // Use array, not stale pointers
}

// SAFE: Don't return freed memory
char* return_safe() {
    char* buffer = malloc(100);
    if (!buffer) return NULL;

    strcpy(buffer, "data");
    return buffer;  // SAFE: Caller must free
}
# SAFE: Python proper resource lifecycle
from datetime import datetime, timedelta
from contextlib import contextmanager

# SAFE: Using context managers
def file_with_context_safe():
    with open('data.txt', 'r') as f:
        content = f.read()
        # File automatically closed after block
        # Cannot accidentally use closed file

# SAFE: Connection pool with state tracking
class SafeConnection:
    def __init__(self, conn):
        self._conn = conn
        self._closed = False

    def execute(self, query):
        if self._closed:
            raise RuntimeError("Connection is closed")
        return self._conn.execute(query)

    def close(self):
        if not self._closed:
            self._conn.close()
            self._closed = True

    @property
    def is_closed(self):
        return self._closed

class SafeConnectionPool:
    @contextmanager
    def get_connection(self):
        conn = SafeConnection(self._pool.get())
        try:
            yield conn
        finally:
            if not conn.is_closed:
                conn.close()
            self._pool.put(conn._conn)

# SAFE: Token with expiration check
class SafeTokenAuth:
    def __init__(self):
        self.token = None
        self.expires_at = None

    def authenticate(self, token, expires_in=3600):
        self.token = token
        self.expires_at = datetime.now() + timedelta(seconds=expires_in)

    def is_expired(self):
        return not self.expires_at or datetime.now() >= self.expires_at

    def get_token(self):
        if self.is_expired():
            raise TokenExpiredError("Token has expired")
        return self.token

    def make_request(self, endpoint):
        if self.is_expired():
            self.refresh_token()  # Refresh before use

        headers = {'Authorization': f'Bearer {self.token}'}
        return requests.get(endpoint, headers=headers)

# SAFE: Safe iteration with copy
def iterate_safe():
    items = [1, 2, 3, 4, 5]
    for item in items[:]:  # Iterate over copy
        if item == 3:
            items.remove(item)  # Safe to modify original
        print(item)

    # Or use list comprehension
    items = [x for x in items if x != 3]
// SAFE: Java proper resource lifecycle
import java.sql.*;
import java.util.*;
import java.time.*;

public class SafeResourceExpiration {

    // SAFE: Use try-with-resources
    public void connectionWithTryResources() throws SQLException {
        try (Connection conn = DriverManager.getConnection(url);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {

            while (rs.next()) {
                process(rs);
            }
            // All resources automatically closed
        }
    }

    // SAFE: Use iterator's remove method
    public void safeIteratorRemoval() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
        Iterator<String> iter = list.iterator();

        while (iter.hasNext()) {
            String item = iter.next();
            if (item.equals("b")) {
                iter.remove();  // SAFE: Uses iterator's remove
            }
        }
    }

    // SAFE: Check session expiration
    private Map<String, Session> sessions = new ConcurrentHashMap<>();

    public Object getData(String sessionId) throws SessionExpiredException {
        Session session = sessions.get(sessionId);

        if (session == null) {
            throw new SessionExpiredException("Session not found");
        }

        // SAFE: Check expiration
        if (session.isExpired()) {
            sessions.remove(sessionId);
            throw new SessionExpiredException("Session expired");
        }

        return session.getData();
    }

    // SAFE: Session with expiration tracking
    public static class Session {
        private final Instant expiresAt;
        private final Object data;

        public Session(Object data, Duration ttl) {
            this.data = data;
            this.expiresAt = Instant.now().plus(ttl);
        }

        public boolean isExpired() {
            return Instant.now().isAfter(expiresAt);
        }

        public Object getData() {
            if (isExpired()) {
                throw new IllegalStateException("Session expired");
            }
            return data;
        }
    }

    // SAFE: Copy before modification
    public void safeClearAndUse() {
        List<User> copy = new ArrayList<>(cachedUsers);
        cachedUsers.clear();

        // SAFE: Work with copy
        for (User user : copy) {
            processUser(user);
        }
    }
}
// SAFE: JavaScript proper resource lifecycle
class SafeResources {

    // SAFE: Track WebSocket state
    createWebSocket() {
        const ws = new WebSocket('ws://example.com');
        let isOpen = false;

        ws.onopen = () => {
            isOpen = true;
            ws.send('Hello');
        };

        ws.onclose = () => {
            isOpen = false;
        };

        return {
            send: (data) => {
                // SAFE: Check state before send
                if (isOpen && ws.readyState === WebSocket.OPEN) {
                    ws.send(data);
                } else {
                    console.warn('WebSocket not ready');
                }
            },
            close: () => {
                if (isOpen) {
                    ws.close();
                }
            }
        };
    }

    // SAFE: JWT with expiration check
    isTokenExpired(token) {
        try {
            const payload = JSON.parse(atob(token.split('.')[1]));
            return Date.now() >= payload.exp * 1000;
        } catch {
            return true;
        }
    }

    async makeAuthenticatedRequest() {
        const token = localStorage.getItem('jwt');

        // SAFE: Check expiration
        if (!token || this.isTokenExpired(token)) {
            await this.refreshToken();
        }

        return fetch('/api/secure', {
            headers: { 'Authorization': `Bearer ${localStorage.getItem('jwt')}` }
        });
    }

    // SAFE: Cleanup on unmount
    componentDidMount() {
        this._mounted = true;
        this.timer = setInterval(() => {
            // SAFE: Check if still mounted
            if (this._mounted) {
                this.setState({ count: this.state.count + 1 });
            }
        }, 1000);
    }

    componentWillUnmount() {
        this._mounted = false;
        if (this.timer) {
            clearInterval(this.timer);
            this.timer = null;
        }
    }

    // SAFE: AbortController for cleanup
    async fetchWithCleanup() {
        const controller = new AbortController();

        // Abort on cleanup
        this.cleanup = () => controller.abort();

        try {
            const response = await fetch('/api/data', {
                signal: controller.signal
            });
            return response.json();
        } catch (e) {
            if (e.name === 'AbortError') {
                console.log('Fetch aborted');
            }
            throw e;
        }
    }

    // SAFE: Remove listeners properly
    setupListener() {
        const button = document.getElementById('myButton');
        const handler = () => {
            console.log('Clicked');
        };

        button.addEventListener('click', handler);

        // Return cleanup function
        return () => {
            button.removeEventListener('click', handler);
        };
    }
}

Exploited in the Wild

Use-After-Free Exploits

Browser and kernel vulnerabilities enabling RCE.

Expired Token Attacks

Continued access after credential revocation.

Session Fixation

Expired sessions allowing unauthorized access.


Tools to test/exploit

  • AddressSanitizer (ASan) for use-after-free.

  • Valgrind for memory issues.

  • Static analyzers for resource tracking.


CVE Examples

  • CVE-2021-21224: Chrome V8 use-after-free.

  • CVE-2020-6819: Firefox use-after-free.


References

  1. MITRE. "CWE-672: Operation on a Resource after Expiration or Release." https://cwe.mitre.org/data/definitions/672.html

  2. Memory safety best practices.