Improper Resource Locking
Description
Improper Resource Locking is a vulnerability where a product does not lock or does not correctly lock a resource when the product must have exclusive access to that resource. Inadequate resource locking enables attackers or concurrent processes to modify resources during active operations. This violation of product assumptions regarding resource stability can produce unexpected behaviors, data corruption, and system failures. The weakness manifests when mutex locks are not acquired, lock acquisition failures are not checked, or synchronization mechanisms are improperly implemented.
Risk
Improper resource locking creates race conditions that enable data corruption and security bypasses. In financial applications, unsynchronized balance operations can result in monetary loss or gain through race conditions. In multi-threaded applications, unprotected shared resources can lead to use-after-free vulnerabilities when one thread frees memory while another is using it. Database operations without proper locking can cause data inconsistencies. The unpredictable nature of race conditions makes these bugs difficult to detect during testing but easily exploitable in production.
Solution
Implement synchronization mechanisms consistently whenever accessing shared resources. Use language-provided synchronization primitives (synchronized keyword in Java, mutexes in C/C++, locks in Python). Always check return values from lock acquisition functions to handle failures gracefully. Use RAII patterns or try-finally blocks to ensure locks are released even when exceptions occur. Consider using lock-free data structures where appropriate. Apply the principle of minimal locking scope—hold locks for the shortest time necessary. Use static analysis tools to detect missing synchronization.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity, Availability Modify Application Data - Attackers may alter application data through race conditions. DoS: Instability - System may become unstable due to concurrent modifications. DoS: Crash, Exit, or Restart - Process termination or restart scenarios may occur. |
Example Code
Vulnerable Code
// Vulnerable: No error checking on mutex lock
#include <pthread.h>
void vulnerable_function(pthread_mutex_t *mutex) {
// Vulnerable: Ignores return value
// Lock may fail, leaving resource unprotected
pthread_mutex_lock(mutex);
/* access shared resource */
modify_shared_data();
pthread_mutex_unlock(mutex);
}
// Vulnerable: No locking at all
int shared_counter = 0;
void vulnerable_increment() {
// Vulnerable: Race condition - not atomic
shared_counter++; // Read-modify-write without lock
}
// Vulnerable: Unsynchronized bank account operations
public class VulnerableBankAccount {
private double accountBalance;
// Vulnerable: No synchronization
public void deposit(double depositAmount) {
// Race condition: another thread may read/write between these operations
double newBalance = accountBalance + depositAmount;
accountBalance = newBalance;
}
// Vulnerable: No synchronization
public void withdraw(double withdrawAmount) {
double newBalance = accountBalance - withdrawAmount;
accountBalance = newBalance;
}
// Vulnerable: Check-then-act race condition
public void transferTo(VulnerableBankAccount other, double amount) {
if (accountBalance >= amount) {
// Another thread may withdraw between check and transfer
this.withdraw(amount);
other.deposit(amount);
}
}
}
# Vulnerable: Unsynchronized shared resource
import threading
class VulnerableCounter:
def __init__(self):
self.count = 0
# Vulnerable: No locking
def increment(self):
# Race condition: read, increment, write not atomic
current = self.count
self.count = current + 1
# Vulnerable: Check-then-act
def increment_if_below(self, limit):
if self.count < limit:
# Another thread may increment between check and increment
self.increment()
Fixed Code
// Fixed: Proper error checking on mutex operations
#include <pthread.h>
#include <errno.h>
int secure_function(pthread_mutex_t *mutex) {
// Fixed: Check return value
int result = pthread_mutex_lock(mutex);
if (result != 0) {
log_error("Failed to acquire mutex: %d", result);
return result; // Handle failure appropriately
}
/* access shared resource */
modify_shared_data();
// Fixed: Check unlock result too
result = pthread_mutex_unlock(mutex);
if (result != 0) {
log_error("Failed to release mutex: %d", result);
}
return result;
}
// Fixed: Atomic operations or proper locking
#include <stdatomic.h>
atomic_int shared_counter = 0;
void secure_increment() {
// Fixed: Atomic operation
atomic_fetch_add(&shared_counter, 1);
}
// Alternative: Use mutex
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
int protected_counter = 0;
void secure_increment_with_mutex() {
pthread_mutex_lock(&counter_mutex);
protected_counter++;
pthread_mutex_unlock(&counter_mutex);
}
// Fixed: Synchronized bank account operations
public class SecureBankAccount {
private double accountBalance;
private final Object balanceLock = new Object();
// Fixed: Synchronized method
public synchronized void deposit(double depositAmount) {
double newBalance = accountBalance + depositAmount;
accountBalance = newBalance;
}
// Fixed: Synchronized method
public synchronized void withdraw(double withdrawAmount) {
double newBalance = accountBalance - withdrawAmount;
accountBalance = newBalance;
}
// Fixed: Atomic check-and-transfer
public synchronized void transferTo(SecureBankAccount other, double amount) {
if (accountBalance >= amount) {
this.withdraw(amount);
other.deposit(amount);
}
}
}
// Alternative: Using ReentrantLock for more control
import java.util.concurrent.locks.ReentrantLock;
public class SecureBankAccountWithLock {
private double balance;
private final ReentrantLock balanceChangeLock = new ReentrantLock();
public void deposit(double amount) {
balanceChangeLock.lock();
try {
balance = balance + amount;
} finally {
// Fixed: Always release in finally block
balanceChangeLock.unlock();
}
}
public void withdraw(double amount) {
balanceChangeLock.lock();
try {
balance = balance - amount;
} finally {
balanceChangeLock.unlock();
}
}
public boolean tryTransfer(SecureBankAccountWithLock other, double amount) {
// Fixed: Try-lock to avoid deadlock
if (balanceChangeLock.tryLock()) {
try {
if (balance >= amount) {
balance -= amount;
other.deposit(amount);
return true;
}
} finally {
balanceChangeLock.unlock();
}
}
return false;
}
}
# Fixed: Properly synchronized shared resource
import threading
class SecureCounter:
def __init__(self):
self.count = 0
self.lock = threading.Lock()
# Fixed: Acquire lock before accessing shared data
def increment(self):
with self.lock: # Automatically acquires and releases
current = self.count
self.count = current + 1
# Fixed: Atomic check-and-increment
def increment_if_below(self, limit):
with self.lock:
if self.count < limit:
self.count += 1
return True
return False
# Fixed: Safe read
def get_count(self):
with self.lock:
return self.count
# Alternative: Using RLock for reentrant locking
class SecureCounterReentrant:
def __init__(self):
self.count = 0
self.lock = threading.RLock() # Reentrant lock
def increment(self):
with self.lock:
self.count += 1
def increment_multiple(self, times):
with self.lock:
for _ in range(times):
self.increment() # Safe: RLock allows reentry
CVE Examples
- CVE-2022-20141 — Operating system kernel insufficient resource locking leading to use-after-free vulnerability.
References
- MITRE Corporation. "CWE-413: Improper Resource Locking." https://cwe.mitre.org/data/definitions/413.html
- Oracle. "Java Concurrency Tutorial - Synchronization." https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html