Race Condition innerhalb eines Threads

Beschreibung

Race Condition innerhalb eines Threads ist eine Schwachstelle, die auftritt, wenn zwei oder mehr Ausführungsthreads gleichzeitig eine Ressource ohne ordnungsgemäße Synchronisation verwenden, was zu undefiniertem Verhalten und inkonsistentem Zustand führt. Während Race Conditions typischerweise mehrere Threads betreffen, konzentriert sich diese Schwäche auf Situationen, in denen das Ressourcenzugriffsmuster innerhalb des logischen Ablaufs eines einzelnen Threads Bedingungen schafft, in denen ein anderer Thread eingreifen kann, oder in denen asynchrone Operationen innerhalb desselben Threads miteinander kollidieren. Das fundamentale Problem ist, dass Daten gelesen oder modifiziert werden können, während sie sich aufgrund gleichzeitigen Zugriffs in einem ungültigen oder inkonsistenten Zustand befinden.

Risiko

Thread-Race-Conditions können Datenkorruption, Sicherheits-Bypasses und Use-After-Free-Schwachstellen verursachen. Wenn ein Thread eine Ressource zerstört, während ein anderer sie verwendet, treten Abstürze oder ausnutzbare Speicherkorruption auf. In Webbrowsern haben Race Conditions zwischen Threads, die dieselbe Ressource handhaben, zu kritischen Sicherheitsschwachstellen geführt. Die Datenintegrität wird kompromittiert, wenn mehrere Threads gemeinsamen Zustand ohne Synchronisation modifizieren - finanzielle Berechnungen, Authentifizierungszustände und Sicherheitsentscheidungen können alle korrumpiert werden. Die intermittierende Natur von Race Conditions macht sie schwer in Tests zu erkennen, aber Angreifer können sie manchmal zuverlässig auslösen.

Lösung

Implementieren Sie ordnungsgemäße Sperrmechanismen um Codeabschnitte, die gemeinsame Daten modifizieren oder lesen. Verwenden Sie Mutexe, Read-Write-Locks oder andere Synchronisationsprimitive, die für das Zugriffsmuster angemessen sind. Erwägen Sie die Verwendung lock-freier Datenstrukturen wo angemessen. Befolgen Sie konsistente Sperrhierarchien, um Deadlocks zu verhindern. Verwenden Sie thread-sichere Datenstrukturen aus Standardbibliotheken. Implementieren Sie Ressourcenbesitzmuster, die klar definieren, welcher Thread zu jedem Zeitpunkt eine Ressource besitzt. Erwägen Sie die Verwendung speichersicherer Sprachen mit integriertem Race-Condition-Schutz. Nutzen Sie statische Analyse und dynamische Tools wie ThreadSanitizer, um Race Conditions zu erkennen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätUmfang: Integrität

Daten können geändert werden, während sie sich in einem inkonsistenten Zustand befinden, was den logischen Ablauf kompromittiert und unerwartetes Verhalten verursacht.
VerfügbarkeitUmfang: Verfügbarkeit

Use-After-Free-Bedingungen aus Race Conditions verursachen Abstürze und Systeminstabilität.

Beispielcode

Anfälliger Code

// Anfällig: Gemeinsame Ressource ohne Synchronisation
typedef struct {
    int *data;
    int size;
    int ref_count;
} SharedBuffer;

SharedBuffer *global_buffer = NULL;

// Thread 1
void vulnerable_use_buffer() {
    if (global_buffer && global_buffer->data) {
        // Anfällig: Ein anderer Thread kann Buffer hier freigeben
        int value = global_buffer->data[0];
        process(value);
    }
}

// Thread 2
void vulnerable_free_buffer() {
    if (global_buffer) {
        free(global_buffer->data);
        global_buffer->data = NULL;  // Race: Thread 1 kann abstürzen
        free(global_buffer);
        global_buffer = NULL;
    }
}
// Anfällig: Check-Then-Act ohne Synchronisation
public class VulnerableCache {
    private Map<String, Object> cache = new HashMap<>();

    public Object get(String key) {
        // Anfällig: Ein anderer Thread kann Key entfernen
        if (cache.containsKey(key)) {
            return cache.get(key);  // Kann null zurückgeben!
        }
        return null;
    }

    public void remove(String key) {
        cache.remove(key);  // Raced mit get()
    }
}
# Anfällig: Gemeinsamer Zähler ohne Lock
import threading

counter = 0

def vulnerable_increment():
    global counter
    # Anfällig: Lesen-Modifizieren-Schreiben ist nicht atomar
    temp = counter
    temp += 1
    counter = temp  # Ein anderer Thread kann counter geändert haben

Korrigierter Code

// Korrigiert: Ordnungsgemäße Synchronisation mit Mutex
#include <pthread.h>

typedef struct {
    int *data;
    int size;
    int ref_count;
    pthread_mutex_t lock;
} SharedBuffer;

SharedBuffer *global_buffer = NULL;
pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;

void secure_use_buffer() {
    pthread_mutex_lock(&buffer_lock);

    if (global_buffer && global_buffer->data) {
        int value = global_buffer->data[0];
        pthread_mutex_unlock(&buffer_lock);
        process(value);
    } else {
        pthread_mutex_unlock(&buffer_lock);
    }
}

void secure_free_buffer() {
    pthread_mutex_lock(&buffer_lock);

    if (global_buffer) {
        free(global_buffer->data);
        free(global_buffer);
        global_buffer = NULL;
    }

    pthread_mutex_unlock(&buffer_lock);
}
// Korrigiert: Thread-sichere Operationen
import java.util.concurrent.ConcurrentHashMap;

public class SecureCache {
    private ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();

    public Object get(String key) {
        // Korrigiert: Atomare Operation
        return cache.get(key);
    }

    public Object getOrCompute(String key, Supplier<Object> supplier) {
        // Korrigiert: Atomares Compute-If-Absent
        return cache.computeIfAbsent(key, k -> supplier.get());
    }

    public void remove(String key) {
        cache.remove(key);
    }
}
# Korrigiert: Thread-sicherer Zähler mit Lock
import threading

counter = 0
counter_lock = threading.Lock()

def secure_increment():
    global counter
    with counter_lock:  # Korrigiert: Atomare Operation
        counter += 1

CVE-Beispiele

  • CVE-2022-2621 — Browser-Race-Condition führte zu Use-After-Free.

Referenzen

  1. MITRE Corporation. "CWE-366: Race Condition within a Thread." https://cwe.mitre.org/data/definitions/366.html
  2. Google. "ThreadSanitizer." https://github.com/google/sanitizers