Operation on Resource in Wrong Phase of Lifetime
Description
Operation on Resource in Wrong Phase of Lifetime occurs when code performs operations on a resource that is inappropriate for the resource's current state in its lifecycle. This includes using a resource before it's ready, after it's been closed, during initialization, or in an invalid state. Common examples include reading from unopened files, writing to closed connections, or calling methods on partially constructed objects.
Risk
Undefined behavior from using resources in invalid states. Data corruption from writing to improperly prepared resources. Security vulnerabilities from bypassing initialization routines. Crashes from operating on deallocated resources. Information disclosure from accessing resources before sanitization. Authentication bypass from using sessions before validation.
Solution
Implement explicit state tracking for resources. Use state machines for complex resource lifecycles. Validate resource state before operations. Use factory patterns to ensure proper initialization. Implement proper cleanup sequences. Use language constructs that enforce lifecycle (RAII, try-with-resources).
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Data Corruption Operations on unprepared resources corrupt data. |
| Availability | Scope: Crashes Invalid state operations cause failures. |
| Security | Scope: Bypass Using resources before security checks complete. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Operating on resources in wrong lifecycle phase
#include <stdio.h>
#include <stdlib.h>
// VULNERABLE: Using before open
FILE* global_log = NULL;
void log_event_vulnerable(const char* message) {
// VULNERABLE: global_log may not be opened yet
fprintf(global_log, "%s\n", message); // Crash if NULL
}
void init_logging() {
global_log = fopen("app.log", "a");
}
// VULNERABLE: Using after close
void process_file_vulnerable(const char* filename) {
FILE* file = fopen(filename, "r");
char buffer[1024];
fread(buffer, 1, sizeof(buffer), file);
fclose(file);
// VULNERABLE: Using file after close
fseek(file, 0, SEEK_SET); // Undefined behavior
fread(buffer, 1, sizeof(buffer), file);
}
// VULNERABLE: Using partially initialized structure
typedef struct {
int (*process)(void* data);
void* data;
int size;
} Handler;
void use_handler_vulnerable(Handler* h) {
// VULNERABLE: process may not be set yet
h->process(h->data); // Crash if process is NULL
}
// VULNERABLE: Socket use before connect
int socket_vulnerable() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
// VULNERABLE: Sending before connect
char msg[] = "Hello";
send(sock, msg, sizeof(msg), 0); // Fails - not connected
// Later: connect(sock, ...);
return sock;
}
// VULNERABLE: Memory use during free
void double_use_vulnerable() {
char* buffer = malloc(100);
strcpy(buffer, "data");
free(buffer);
// VULNERABLE: Memory is being freed, not safe
// Even within same function after free
memset(buffer, 0, 100); // Use after free
}
# VULNERABLE: Python resource lifecycle issues
class VulnerableConnection:
def __init__(self):
self.socket = None
self.connected = False
def send(self, data):
# VULNERABLE: May not be connected
self.socket.send(data) # AttributeError or socket error
def connect(self, host, port):
self.socket = socket.socket()
self.socket.connect((host, port))
self.connected = True
# VULNERABLE: Using database before connection
class VulnerableDatabase:
def __init__(self):
self.connection = None
self.cursor = None
def query(self, sql):
# VULNERABLE: cursor may not exist
self.cursor.execute(sql) # AttributeError
return self.cursor.fetchall()
def connect(self, connstr):
self.connection = psycopg2.connect(connstr)
self.cursor = self.connection.cursor()
# VULNERABLE: Using closed resource
def process_file_vulnerable(filename):
f = open(filename, '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: Using session before authentication
class VulnerableSession:
def __init__(self):
self.user = None
self.permissions = None
def get_user_data(self):
# VULNERABLE: May be called before authenticate()
return {
'username': self.user.username, # AttributeError
'permissions': self.permissions
}
def authenticate(self, username, password):
if verify_credentials(username, password):
self.user = load_user(username)
self.permissions = load_permissions(username)
// VULNERABLE: Java resource lifecycle issues
public class VulnerableLifecycle {
private Connection connection;
private PreparedStatement statement;
// VULNERABLE: Using before initialization
public ResultSet query(String sql) throws SQLException {
// VULNERABLE: connection may be null
statement = connection.prepareStatement(sql);
return statement.executeQuery();
}
public void connect(String url) throws SQLException {
connection = DriverManager.getConnection(url);
}
// VULNERABLE: Using after close
public void processAndClose() throws SQLException {
ResultSet rs = statement.executeQuery();
connection.close();
// VULNERABLE: Using connection after close
while (rs.next()) { // May fail - connection closed
process(rs);
}
}
// VULNERABLE: Builder used before complete
public static class VulnerableBuilder {
private String host;
private int port;
private Config config;
public void connect() {
// VULNERABLE: config may not be set
Socket socket = new Socket(host, port);
socket.setSoTimeout(config.getTimeout()); // NPE
}
public VulnerableBuilder host(String h) {
this.host = h;
return this;
}
public VulnerableBuilder config(Config c) {
this.config = c;
return this;
}
}
}
// VULNERABLE: Using object during construction
public class VulnerableDuringConstruction {
private final List<String> items = new ArrayList<>();
private Thread workerThread;
public VulnerableDuringConstruction() {
// VULNERABLE: Starting thread that uses 'this' before construction complete
workerThread = new Thread(() -> {
// 'this' may not be fully constructed
processItems();
});
workerThread.start();
}
public void processItems() {
for (String item : items) { // items may be null
process(item);
}
}
}
// VULNERABLE: JavaScript resource lifecycle issues
class VulnerableService {
constructor() {
this.db = null;
this.initialized = false;
}
// VULNERABLE: Using before initialization
async query(sql) {
// VULNERABLE: db may be null
return await this.db.query(sql);
}
async initialize() {
this.db = await connectDatabase();
this.initialized = true;
}
// VULNERABLE: Using after disposal
async dispose() {
await this.db.close();
this.db = null;
}
async fetchData() {
// Called after dispose
// VULNERABLE: db is null
return await this.db.query('SELECT * FROM data');
}
}
// VULNERABLE: WebSocket used in wrong state
class VulnerableWebSocket {
constructor(url) {
this.ws = new WebSocket(url);
}
// VULNERABLE: Sending before connection open
send(message) {
// VULNERABLE: WebSocket may not be OPEN yet
this.ws.send(JSON.stringify(message));
// Error: Still in CONNECTING state
}
}
// VULNERABLE: Promise-based initialization not awaited
class VulnerableAsync {
constructor() {
this.data = null;
this.init(); // VULNERABLE: Not awaited
}
async init() {
this.data = await fetchData(); // Takes time
}
process() {
// VULNERABLE: May be called before init completes
return this.data.map(item => item.value); // data is null
}
}
Fixed Code
// SAFE: Proper lifecycle management in C
#include <stdio.h>
#include <stdlib.h>
// SAFE: State tracking for resources
typedef enum {
LOG_UNINITIALIZED,
LOG_OPEN,
LOG_CLOSED
} LogState;
FILE* global_log = NULL;
LogState log_state = LOG_UNINITIALIZED;
void init_logging() {
global_log = fopen("app.log", "a");
if (global_log) {
log_state = LOG_OPEN;
}
}
void log_event_safe(const char* message) {
// SAFE: Check state before use
if (log_state != LOG_OPEN || global_log == NULL) {
return; // Or initialize, or error
}
fprintf(global_log, "%s\n", message);
}
// SAFE: Proper resource lifecycle
void process_file_safe(const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) return;
char buffer[1024];
fread(buffer, 1, sizeof(buffer), file);
// Process while file is open
process_data(buffer);
// SAFE: Close when done, don't use after
fclose(file);
file = NULL;
}
// SAFE: Validate handler state
typedef struct {
int (*process)(void* data);
void* data;
int size;
int initialized;
} SafeHandler;
int use_handler_safe(SafeHandler* h) {
// SAFE: Validate before use
if (!h || !h->initialized || !h->process) {
return -1;
}
return h->process(h->data);
}
SafeHandler* create_handler(int (*proc)(void*), void* data, int size) {
SafeHandler* h = calloc(1, sizeof(SafeHandler));
if (!h) return NULL;
h->process = proc;
h->data = data;
h->size = size;
h->initialized = 1; // Mark as ready
return h;
}
// SAFE: Proper socket lifecycle
int socket_safe() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) return -1;
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
// SAFE: Connect before send
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(sock);
return -1;
}
// SAFE: Socket is connected
char msg[] = "Hello";
send(sock, msg, sizeof(msg), 0);
return sock;
}
# SAFE: Python with proper lifecycle management
from enum import Enum, auto
import contextlib
class ConnectionState(Enum):
DISCONNECTED = auto()
CONNECTING = auto()
CONNECTED = auto()
CLOSED = auto()
class SafeConnection:
def __init__(self):
self.socket = None
self.state = ConnectionState.DISCONNECTED
def send(self, data):
# SAFE: Validate state
if self.state != ConnectionState.CONNECTED:
raise RuntimeError(f"Cannot send in state {self.state}")
self.socket.send(data)
def connect(self, host, port):
if self.state != ConnectionState.DISCONNECTED:
raise RuntimeError("Already connected or connecting")
self.state = ConnectionState.CONNECTING
try:
self.socket = socket.socket()
self.socket.connect((host, port))
self.state = ConnectionState.CONNECTED
except Exception:
self.state = ConnectionState.DISCONNECTED
raise
def close(self):
if self.socket:
self.socket.close()
self.state = ConnectionState.CLOSED
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
# SAFE: Context manager for proper lifecycle
class SafeDatabase:
def __init__(self, connstr):
self.connstr = connstr
self.connection = None
self.cursor = None
def connect(self):
self.connection = psycopg2.connect(self.connstr)
self.cursor = self.connection.cursor()
return self
def query(self, sql, params=None):
if not self.cursor:
raise RuntimeError("Not connected - call connect() first")
self.cursor.execute(sql, params)
return self.cursor.fetchall()
def close(self):
if self.cursor:
self.cursor.close()
if self.connection:
self.connection.close()
def __enter__(self):
return self.connect()
def __exit__(self, *args):
self.close()
# SAFE: Using context manager
def process_database():
with SafeDatabase("host=localhost") as db:
results = db.query("SELECT * FROM users")
# db automatically closed after block
# SAFE: Session with state validation
class SafeSession:
def __init__(self):
self.user = None
self.permissions = None
self.authenticated = False
def authenticate(self, username, password):
if verify_credentials(username, password):
self.user = load_user(username)
self.permissions = load_permissions(username)
self.authenticated = True
return True
return False
def get_user_data(self):
# SAFE: Validate state
if not self.authenticated:
raise RuntimeError("Session not authenticated")
return {
'username': self.user.username,
'permissions': self.permissions
}
// SAFE: Java with proper lifecycle management
public class SafeLifecycle implements AutoCloseable {
private Connection connection;
private PreparedStatement statement;
private boolean connected = false;
public void connect(String url) throws SQLException {
connection = DriverManager.getConnection(url);
connected = true;
}
public ResultSet query(String sql) throws SQLException {
// SAFE: Validate state
if (!connected || connection == null) {
throw new IllegalStateException("Not connected");
}
statement = connection.prepareStatement(sql);
return statement.executeQuery();
}
// SAFE: Process before close
public void processAndClose() throws SQLException {
try {
ResultSet rs = statement.executeQuery();
while (rs.next()) {
process(rs);
}
} finally {
close();
}
}
@Override
public void close() throws SQLException {
connected = false;
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
// SAFE: Builder with validation
public static class SafeBuilder {
private String host;
private int port;
private Config config;
private boolean hostSet = false;
private boolean portSet = false;
public SafeBuilder host(String h) {
this.host = h;
this.hostSet = true;
return this;
}
public SafeBuilder port(int p) {
this.port = p;
this.portSet = true;
return this;
}
public SafeBuilder config(Config c) {
this.config = c;
return this;
}
public Connection build() throws IllegalStateException {
// SAFE: Validate all required fields set
if (!hostSet || !portSet) {
throw new IllegalStateException("Host and port required");
}
Config c = config != null ? config : Config.defaults();
return new Connection(host, port, c);
}
}
// SAFE: Avoid publishing 'this' during construction
public class SafeDuringConstruction {
private final List<String> items = new ArrayList<>();
public SafeDuringConstruction() {
// Don't start threads in constructor
}
// Factory method for proper initialization
public static SafeDuringConstruction create() {
SafeDuringConstruction instance = new SafeDuringConstruction();
instance.startWorker(); // After construction complete
return instance;
}
private void startWorker() {
Thread workerThread = new Thread(this::processItems);
workerThread.start();
}
}
// SAFE: JavaScript with proper lifecycle
class SafeService {
constructor() {
this.db = null;
this.state = 'uninitialized';
}
async initialize() {
this.state = 'initializing';
this.db = await connectDatabase();
this.state = 'ready';
}
async query(sql) {
// SAFE: Validate state
if (this.state !== 'ready') {
throw new Error(`Service not ready: ${this.state}`);
}
return await this.db.query(sql);
}
async dispose() {
if (this.db) {
await this.db.close();
this.db = null;
}
this.state = 'disposed';
}
// Factory method ensures initialization
static async create() {
const service = new SafeService();
await service.initialize();
return service;
}
}
// SAFE: WebSocket with state handling
class SafeWebSocket {
constructor(url) {
this.ws = new WebSocket(url);
this.ready = false;
this.queue = [];
this.ws.onopen = () => {
this.ready = true;
// Send queued messages
this.queue.forEach(msg => this.ws.send(msg));
this.queue = [];
};
}
send(message) {
const data = JSON.stringify(message);
if (this.ready && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
} else {
// Queue for when ready
this.queue.push(data);
}
}
}
// SAFE: Async initialization with factory
class SafeAsync {
constructor() {
this.data = null;
}
async init() {
this.data = await fetchData();
}
process() {
if (!this.data) {
throw new Error('Not initialized');
}
return this.data.map(item => item.value);
}
static async create() {
const instance = new SafeAsync();
await instance.init();
return instance;
}
}
Exploited in the Wild
Use-After-Close Exploits
Database connections used after close causing data corruption.
Pre-Initialization Bypass
Security checks bypassed by using resources before initialization.
Double-Free via Lifecycle
Memory corruption from incorrect lifecycle management.
Tools to test/exploit
-
State machine testing frameworks.
-
Static analysis for lifecycle violations.
-
Fuzzing with lifecycle-aware oracles.
CVE Examples
-
CVE-2019-11477: TCP resource lifecycle issues (SACK panic).
-
CVE-2020-12654: Use of uninitialized wireless driver structures.
References
-
MITRE. "CWE-666: Operation on Resource in Wrong Phase of Lifetime." https://cwe.mitre.org/data/definitions/666.html
-
State machine design patterns.