Entsperren einer Ressource, die nicht gesperrt ist

Beschreibung

Entsperren einer Ressource, die nicht gesperrt ist, ist eine Synchronisations-Schwachstelle, bei der Software versucht, eine Sperre auf einer Ressource freizugeben, die derzeit nicht gesperrt ist, oder die nicht vom aktuellen Thread oder Prozess gesperrt ist. Je nach Locking-Implementierung kann dies den internen Zustand der Sperre, die zugehörige Ressource oder Metadaten, die zur Verfolgung von Sperren verwendet werden, beschädigen. Einige Locking-Mechanismen führen Referenzzählungen oder Eigentümerschaftsinformationen, die beschädigt werden, wenn unlock unangemessen aufgerufen wird. Diese Schwachstelle tritt oft in Fehlerbehandlungspfaden auf, wo Sperren freigegeben werden, ohne zu verifizieren, dass sie erfolgreich erworben wurden.

Risiko

Die Konsequenzen reichen von Speicherbeschädigung über Denial of Service bis hin zu potenzieller Codeausführung. Wenn der Sperrzustand beschädigt ist, können nachfolgende legitime Lock-Operationen fehlschlagen oder unvorhersehbar verhalten, was Abstürze oder Hänger verursacht. In Implementierungen, wo Lock-Metadaten im Speicher gespeichert werden, kann das Entsperren einer nicht gesperrten Ressource benachbarten Speicher beschädigen und möglicherweise Ausnutzung ermöglichen. Im Kernel-Code ist diese Schwachstelle besonders schwerwiegend, da sie zu Systeminstabilität oder Privilegieneskalation führen kann. Race Conditions können auch entstehen, wenn der Sperrzustand inkonsistent wird und nebenläufigen Zugriff auf geschützte Ressourcen ermöglicht.

Lösung

Verifizieren Sie immer, dass eine Sperre gehalten wird, bevor Sie sie entsperren. Verfolgen Sie den Erfolg des Lock-Erwerbs mit booleschen Flags oder Rückgabewert-Prüfungen. Verwenden Sie RAII-Muster (C++) oder try-finally-Blöcke (Java, Python), um sicherzustellen, dass Lock-Freigabe nur erfolgt, wenn Erwerb erfolgreich war. Implementieren Sie Assertions in Debug-Builds, um Lock-Eigentümerschaft vor der Freigabe zu verifizieren. Verwenden Sie Lock-Abstraktionen, die Eigentümerschaft verfolgen und Double-Unlock- oder Unlock-ohne-Lock-Fehler erkennen. Erwägen Sie in C/C++ die Verwendung statischer Analysetools zur Erkennung unzulässiger Lock/Unlock-Muster. Überprüfen Sie Fehlerbehandlungspfade sorgfältig, um sicherzustellen, dass Sperren nur freigegeben werden, wenn sie tatsächlich gehalten werden.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz, Beendigung oder Neustart - Beschädigter Sperrzustand kann Abstürze oder Systemhänger verursachen, wenn nachfolgende Lock-Operationen fehlschlagen.
IntegritätBereich: Integrität

Speicher modifizieren - Entsperrmechanismen können Speicher während der Ausführung beschädigen, wenn sie auf nicht gesperrten Ressourcen operieren.
Integrität, Vertraulichkeit, VerfügbarkeitBereich: Integrität, Vertraulichkeit, Verfügbarkeit

Unerlaubten Code oder Befehle ausführen - In bestimmten Implementierungen könnte Speicherbeschädigung durch unzulässiges Entsperren Codeausführung ermöglichen.

Beispielcode

Anfälliger Code

// Anfällig: Entsperren im Fehlerpfad ohne Verifizierung, dass Lock erworben wurde
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int vulnerable_process(void *data) {
    int result;

    // Versuch zu sperren - könnte fehlschlagen
    result = pthread_mutex_trylock(&mutex);

    // Daten verarbeiten
    if (process_data(data) < 0) {
        // Anfällig: Entsperren auch wenn trylock fehlschlug!
        pthread_mutex_unlock(&mutex);
        return -1;
    }

    // Anfällig: Entsperren hier auch unabhängig vom Lock-Erfolg
    pthread_mutex_unlock(&mutex);
    return 0;
}
// Anfällig: Double unlock
#include <pthread.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void vulnerable_cleanup(int error_occurred) {
    if (error_occurred) {
        // Erstes unlock im Fehlerpfad
        pthread_mutex_unlock(&lock);
    }

    // Anfällig: Bedingungsloses unlock - kann zweites unlock sein!
    pthread_mutex_unlock(&lock);
}
# Anfällig: Unlock ohne passendes Lock
import threading

lock = threading.Lock()

def vulnerable_function(should_lock):
    if should_lock:
        lock.acquire()

    do_work()

    # Anfällig: Gibt immer frei, auch wenn nie erworben
    lock.release()  # RuntimeError wenn Lock nicht erworben wurde
// Anfällig: Entsperren in finally ohne Prüfung ob gesperrt
import java.util.concurrent.locks.ReentrantLock;

public class VulnerableUnlock {
    private final ReentrantLock lock = new ReentrantLock();

    public void vulnerableMethod() {
        try {
            // tryLock könnte false zurückgeben
            boolean acquired = lock.tryLock();

            doWork();
        } finally {
            // Anfällig: Entsperrt auch wenn tryLock false zurückgab
            lock.unlock();  // IllegalMonitorStateException wenn nicht gehalten
        }
    }
}

Korrigierter Code

// Korrigiert: Lock-Zustand verfolgen und vor unlock verifizieren
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int fixed_process(void *data) {
    int result;
    int lock_held = 0;  // Korrigiert: Lock-Zustand verfolgen

    result = pthread_mutex_trylock(&mutex);
    if (result == 0) {
        lock_held = 1;  // Lock erfolgreich erworben
    }

    if (process_data(data) < 0) {
        // Korrigiert: Nur entsperren wenn wir das Lock halten
        if (lock_held) {
            pthread_mutex_unlock(&mutex);
        }
        return -1;
    }

    // Korrigiert: Nur entsperren wenn wir das Lock halten
    if (lock_held) {
        pthread_mutex_unlock(&mutex);
    }
    return 0;
}
# Korrigiert: Context Manager für korrekte Lock-Behandlung verwenden
import threading

lock = threading.Lock()

def fixed_function(should_lock):
    if should_lock:
        # Korrigiert: Context Manager stellt korrekte acquire/release-Paarung sicher
        with lock:
            do_work()
    else:
        do_work()

# Alternative: Lock-Zustand explizit verfolgen
def fixed_function_explicit(should_lock):
    acquired = False
    try:
        if should_lock:
            lock.acquire()
            acquired = True

        do_work()
    finally:
        # Korrigiert: Nur freigeben wenn wir erworben haben
        if acquired:
            lock.release()
// Korrigiert: Lock-Eigentümerschaft vor unlock prüfen
import java.util.concurrent.locks.ReentrantLock;

public class FixedUnlock {
    private final ReentrantLock lock = new ReentrantLock();

    public void fixedMethod() {
        boolean acquired = false;
        try {
            acquired = lock.tryLock();

            doWork();
        } finally {
            // Korrigiert: Nur entsperren wenn wir erfolgreich erworben haben
            if (acquired) {
                lock.unlock();
            }
        }
    }

    // Besser: try-with-resources-Muster verwenden
    public void betterMethod() {
        lock.lock();  // Blockierendes Lock garantiert Erwerb
        try {
            doWork();
        } finally {
            lock.unlock();  // Sicher weil lock() erfolgreich war
        }
    }
}
// Beste Praxis: RAII-Wrapper in C++
#include <mutex>

class FixedRAII {
    std::mutex mutex_;

public:
    void safeMethod() {
        // std::lock_guard stellt sicher, dass unlock nur erfolgt wenn lock erfolgreich war
        std::lock_guard<std::mutex> guard(mutex_);
        doWork();
    }  // Automatisches unlock wenn guard out of scope geht

    void tryLockMethod() {
        // std::unique_lock mit try_to_lock behandelt bedingtes Locking
        std::unique_lock<std::mutex> lock(mutex_, std::try_to_lock);

        if (lock.owns_lock()) {
            doWork();
        }  // Entsperrt nur wenn Lock erworben wurde
    }
};

CVE-Beispiele

  • CVE-2010-4210: Kernel-Panic verursacht durch Entsperren einer Ressource, die während des Firmware-Ladens nicht gesperrt war.
  • CVE-2008-4302: Unzulässiges Unlock in Dateisystem-Code führte zu Systemhänger.
  • CVE-2009-1243: Kernel-Schwachstelle durch Entsperren eines nicht gesperrten Spinlocks.

Verwandte CWEs

  • CWE-667: Unzulässiges Locking (Eltern)
  • CWE-765: Mehrfaches Entsperren einer kritischen Ressource (verwandt)
  • CWE-764: Mehrfaches Sperren einer kritischen Ressource (verwandt)
  • CWE-755: Unzureichende Behandlung von Ausnahmebedingungen (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-832: Unlock of a Resource that is not Locked." https://cwe.mitre.org/data/definitions/832.html
  2. CERT C Secure Coding Standard. "CON31-C. Do not destroy a mutex while it is locked."
  3. Linux Kernel Documentation. "Locking."