Missing Lock Check

Description

Missing Lock Check is a vulnerability where a product does not check to see if a lock is present before performing sensitive operations on a resource. The weakness occurs when software fails to verify that protective locking mechanisms are in place before accessing or modifying critical resources, creating opportunities for unauthorized access, data corruption, or race conditions. Unlike improper locking where locks are acquired incorrectly, this weakness involves completely failing to verify lock status before proceeding with operations.

Risk

Missing lock checks enable attackers to access or modify resources that should be exclusively controlled. In security-critical operations, bypassing lock verification can lead to privilege escalation or security control circumvention. Concurrent access to unverified resources can corrupt data integrity. In file systems, operations on unlocked files may interfere with other processes. Database operations without lock verification can cause transaction anomalies. The impact ranges from data corruption to complete system compromise depending on the resource's sensitivity.

Solution

Implement a reliable lock mechanism and always verify lock status before performing sensitive operations. Use language-provided locking primitives that combine verification and acquisition atomically. Design APIs that require lock ownership proof before allowing resource access. Implement lock verification at the beginning of every function that accesses protected resources. Use assertions or runtime checks to verify lock invariants during development. Consider using lock-guarded accessor patterns that make unverified access impossible by design.

Common Consequences

ImpactDetails
IntegrityScope: Integrity, Availability

Modify Application Data - Attackers may alter application data by accessing resources without proper lock verification. DoS: Instability - System may become unstable due to concurrent uncontrolled access. DoS: Crash, Exit, or Restart - Unexpected state corruption can cause system failures.

Example Code

Vulnerable Code

// Vulnerable: No lock check before accessing protected resource
#include <pthread.h>

typedef struct {
    int data;
    pthread_mutex_t lock;
    int is_locked;
} ProtectedResource;

// Vulnerable: Doesn't verify lock is held
void vulnerable_modify_data(ProtectedResource *res, int new_value) {
    // Vulnerable: No check if lock is actually held
    // Assumes caller holds lock, but doesn't verify
    res->data = new_value;
}

// Vulnerable: Inconsistent lock checking
void vulnerable_operation(ProtectedResource *res) {
    // Sometimes locks, sometimes doesn't
    if (some_condition) {
        pthread_mutex_lock(&res->lock);
    }

    // Vulnerable: Proceeds without knowing if locked
    res->data++;

    if (some_condition) {
        pthread_mutex_unlock(&res->lock);
    }
}
# Vulnerable: No lock verification before file operations
import fcntl

class VulnerableFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.file = None

    def open_file(self):
        self.file = open(self.filepath, 'r+')

    # Vulnerable: Doesn't check if file is locked
    def write_data(self, data):
        # Vulnerable: No verification that we hold the lock
        # Another process may have the lock
        self.file.write(data)
        self.file.flush()

    # Vulnerable: Assumes lock without checking
    def modify_sensitive_data(self, data):
        # Should verify lock is held, but doesn't
        self.file.seek(0)
        self.file.write(data)
// Vulnerable: No lock verification in protected method
public class VulnerableProtectedResource {
    private Object data;
    private final Object lock = new Object();
    private volatile boolean isLocked = false;

    public void acquireLock() {
        synchronized(lock) {
            isLocked = true;
        }
    }

    public void releaseLock() {
        synchronized(lock) {
            isLocked = false;
        }
    }

    // Vulnerable: Doesn't verify lock is held
    public void modifyData(Object newData) {
        // Vulnerable: No check for isLocked
        // Caller may have forgotten to acquire lock
        this.data = newData;
    }

    // Vulnerable: Check is not atomic with operation
    public void unsafeModify(Object newData) {
        if (isLocked) {
            // Vulnerable: Lock may be released between check and modify
            this.data = newData;
        }
    }
}

Fixed Code

// Fixed: Always verify lock is held before operations
#include <pthread.h>
#include <assert.h>

typedef struct {
    int data;
    pthread_mutex_t lock;
    pthread_t lock_owner;
    int is_locked;
} ProtectedResource;

// Fixed: Verify lock ownership before modification
int secure_modify_data(ProtectedResource *res, int new_value) {
    // Fixed: Verify this thread holds the lock
    if (!res->is_locked || !pthread_equal(res->lock_owner, pthread_self())) {
        return -1;  // Error: lock not held by this thread
    }

    res->data = new_value;
    return 0;
}

// Fixed: Proper lock acquisition with ownership tracking
int acquire_lock(ProtectedResource *res) {
    int result = pthread_mutex_lock(&res->lock);
    if (result == 0) {
        res->is_locked = 1;
        res->lock_owner = pthread_self();
    }
    return result;
}

int release_lock(ProtectedResource *res) {
    // Fixed: Verify we own the lock before releasing
    if (!pthread_equal(res->lock_owner, pthread_self())) {
        return -1;
    }

    res->is_locked = 0;
    return pthread_mutex_unlock(&res->lock);
}

// Fixed: Combined lock-check-and-operate pattern
int secure_operation(ProtectedResource *res, int delta) {
    if (acquire_lock(res) != 0) {
        return -1;
    }

    // Lock is definitely held here
    res->data += delta;

    release_lock(res);
    return 0;
}
# Fixed: Verify lock before operations
import fcntl
import threading

class SecureFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.file = None
        self.lock_held = False
        self._internal_lock = threading.Lock()

    def open_file(self):
        self.file = open(self.filepath, 'r+')

    def acquire_lock(self):
        with self._internal_lock:
            fcntl.flock(self.file.fileno(), fcntl.LOCK_EX)
            self.lock_held = True

    def release_lock(self):
        with self._internal_lock:
            if self.lock_held:
                fcntl.flock(self.file.fileno(), fcntl.LOCK_UN)
                self.lock_held = False

    # Fixed: Verify lock before writing
    def write_data(self, data):
        with self._internal_lock:
            if not self.lock_held:
                raise RuntimeError("Lock not held - call acquire_lock() first")
            self.file.write(data)
            self.file.flush()

    # Fixed: Context manager for safe lock-protected operations
    def __enter__(self):
        self.open_file()
        self.acquire_lock()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release_lock()
        if self.file:
            self.file.close()


# Usage: Lock verification is automatic
with SecureFileHandler('/path/to/file') as handler:
    handler.write_data("safe data")  # Lock guaranteed to be held
// Fixed: Lock verification enforced by design
import java.util.concurrent.locks.ReentrantLock;

public class SecureProtectedResource {
    private Object data;
    private final ReentrantLock lock = new ReentrantLock();

    // Fixed: Private method that verifies lock is held
    private void verifyLockHeld() {
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("Lock must be held to perform this operation");
        }
    }

    // Fixed: Public method that requires lock verification
    public void modifyData(Object newData) {
        verifyLockHeld();  // Fixed: Always verify
        this.data = newData;
    }

    public Object getData() {
        verifyLockHeld();  // Fixed: Even reads require verification
        return this.data;
    }

    // Fixed: Safe public interface that handles locking
    public void safeModify(Object newData) {
        lock.lock();
        try {
            // Lock is definitely held here
            this.data = newData;
        } finally {
            lock.unlock();
        }
    }

    // Fixed: Provide a locked operation callback pattern
    public <T> T withLock(java.util.function.Supplier<T> operation) {
        lock.lock();
        try {
            return operation.get();
        } finally {
            lock.unlock();
        }
    }
}

// Usage
// resource.withLock(() -> {
//     resource.modifyData(newValue);  // Lock guaranteed
//     return resource.getData();
// });

CVE Examples

  • CVE-2004-1056 — Product does not properly check if a lock is present, allowing attackers to access functionality that should be protected.

References

  1. MITRE Corporation. "CWE-414: Missing Lock Check." https://cwe.mitre.org/data/definitions/414.html
  2. CERT C Coding Standard. "CON00-C. Avoid race conditions with multiple threads." https://wiki.sei.cmu.edu/confluence/display/c/CON00-C.+Avoid+race+conditions+with+multiple+threads