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

ImpactDetails
IntegrityScope: 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

  1. MITRE Corporation. "CWE-413: Improper Resource Locking." https://cwe.mitre.org/data/definitions/413.html
  2. Oracle. "Java Concurrency Tutorial - Synchronization." https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html