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
| Impact | Details |
|---|---|
| Integrity | Scope: Memory Corruption Use-after-free corrupts memory state. |
| Confidentiality | Scope: Information Disclosure Released memory may contain sensitive data. |
| Authorization | Scope: 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
-
MITRE. "CWE-672: Operation on a Resource after Expiration or Release." https://cwe.mitre.org/data/definitions/672.html
-
Memory safety best practices.