Signal-Handler-Race-Condition

Beschreibung

Signal-Handler-Race-Condition ist eine Schwachstelle, die auftritt, wenn Signal-Handler durch asynchrone Aktionen Race Conditions einführen. Signale können normale Programmausführung an praktisch jedem Punkt unterbrechen und Signal-Handler im Kontext des unterbrochenen Codes ausführen. Wenn Signal-Handler nicht-reentrant-Funktionen verwenden, globale Variablen modifizieren oder zustandssensitive Operationen durchführen, können sie Annahmen verletzen, die vom unterbrochenen Code oder anderen Signal-Handlern gemacht wurden. Das Kernproblem ist, dass Signale während kritischer Abschnitte ankommen können, in denen Datenstrukturen in inkonsistenten Zuständen sind, was zu Korruption, Abstürzen oder ausnutzbaren Bedingungen führt, wenn der Handler auf diese Strukturen zugreift.

Risiko

Signal-Handler-Race-Conditions können zu schwerwiegenden Sicherheitsschwachstellen führen, einschließlich beliebiger Codeausführung, Privilegieneskalation und Denial-of-Service. Wenn Handler Funktionen wie malloc/free aufrufen, während das Hauptprogramm ebenfalls in malloc/free ist, können Double-Free- oder Use-After-Free-Bedingungen auftreten. Handler, die globalen Zustand modifizieren, können Datenstrukturen korrumpieren. Angreifer können remote Signale auslösen (wie SIGURG für dringende TCP-Daten), um diese Bedingungen auszunutzen. Das unvorhersagbare Timing macht diese Schwachstellen schwer reproduzierbar, aber Angreifer können ihre Chancen durch wiederholte Versuche oder durch Kontrolle des Signaleintrittszeitpunkts erhöhen.

Lösung

Halten Sie Signal-Handler minimal - idealerweise setzen sie nur ein Flag, das das Hauptprogramm prüft. Rufen Sie nur async-signal-sichere Funktionen aus Handlern auf (dokumentiert in signal-safety(7) unter Linux). Blockieren Sie Signale während kritischer Abschnitte mit sigprocmask. Vermeiden Sie den Zugriff auf globale Variablen in Handlern, es sei denn, sie sind volatile sig_atomic_t. Verwenden Sie sigaction mit SA_RESTART, um unterbrochene Systemaufrufe zu behandeln. Erwägen Sie die Verwendung von signalfd() unter Linux, um Signale synchron zu behandeln. Rufen Sie niemals malloc, free, printf oder andere nicht-reentrant-Funktionen aus Signal-Handlern auf.

Häufige Auswirkungen

AuswirkungDetails
IntegritätUmfang: Integrität

Datenkorruption und mögliche beliebige Codeausführung durch Modifikation globaler Variablen zu unerwarteten Zeiten.
VerfügbarkeitUmfang: Verfügbarkeit

Systemabstürze durch Double-Free oder korrumpierte Datenstrukturen, wenn Handler kritische Abschnitte unterbrechen.
ZugriffskontrolleUmfang: Zugriffskontrolle

Privilegieneskalation, wenn Handler Code unterbrechen, der mit erhöhten Privilegien läuft.

Beispielcode

Anfälliger Code

// Anfällig: Nicht-reentrant-Funktion in Signal-Handler
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

char *global_buffer = NULL;

void vulnerable_handler(int sig) {
    // Anfällig: free() ist nicht async-signal-sicher
    // Wenn Signal während malloc/free ankommt, tritt Korruption auf
    free(global_buffer);
    global_buffer = malloc(100);  // Ebenfalls unsicher

    // Anfällig: printf ist nicht async-signal-sicher
    printf("Signal empfangen\n");
}

int main() {
    signal(SIGINT, vulnerable_handler);

    while (1) {
        // Wenn Signal hier ankommt, während malloc, kann Double-Free auftreten
        global_buffer = malloc(1000);
        // ... Buffer verwenden ...
        free(global_buffer);
    }
}
// Anfällig: Globale Zustandsmodifikation in Handler
volatile int counter = 0;
volatile int processing = 0;

void vulnerable_handler(int sig) {
    // Anfällig: Nicht-atomares Lesen-Modifizieren-Schreiben
    if (processing) {
        counter++;  // Kann mit Hauptprogramm racen
    }
}

void process_data() {
    processing = 1;
    // Anfällig: Signal kann zwischen diesen Anweisungen ankommen
    counter = 0;
    // ... verarbeiten ...
    int result = counter;  // Kann unerwarteten Wert haben
    processing = 0;
}

Korrigierter Code

// Korrigiert: Minimaler Signal-Handler mit Flag
#include <signal.h>
#include <stdlib.h>
#include <string.h>

volatile sig_atomic_t signal_received = 0;

void secure_handler(int sig) {
    // Korrigiert: Nur Flag setzen - async-signal-sicher
    signal_received = 1;
}

int main() {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = secure_handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);

    char *buffer = NULL;
    while (1) {
        // Korrigiert: Flag in Hauptschleife prüfen
        if (signal_received) {
            signal_received = 0;
            // Signal sicher im Hauptkontext behandeln
            if (buffer) free(buffer);
            buffer = malloc(100);
        }

        // Normale Verarbeitung mit blockierten Signalen während kritischem Abschnitt
        sigset_t block_set, old_set;
        sigemptyset(&block_set);
        sigaddset(&block_set, SIGINT);

        sigprocmask(SIG_BLOCK, &block_set, &old_set);
        // Kritischer Abschnitt - Signale blockiert
        buffer = realloc(buffer, 1000);
        sigprocmask(SIG_SETMASK, &old_set, NULL);
    }
}
// Korrigiert: signalfd für synchrone Signalbehandlung verwenden (Linux)
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>

int main() {
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);

    // Signale blockieren, damit sie über signalfd geliefert werden
    sigprocmask(SIG_BLOCK, &mask, NULL);

    // signalfd erstellen
    int sfd = signalfd(-1, &mask, 0);

    while (1) {
        struct signalfd_siginfo fdsi;
        ssize_t s = read(sfd, &fdsi, sizeof(fdsi));

        if (s == sizeof(fdsi)) {
            // Korrigiert: Signal synchron behandeln - alle Funktionen sicher
            printf("Signal %d empfangen\n", fdsi.ssi_signo);
            // Kann hier sicher jede Funktion aufrufen
        }
    }
}

CVE-Beispiele

  • CVE-1999-0035 — Signal-Handler ermöglichte Zugriff auf Dateien mit erhöhten Privilegien.
  • CVE-2001-0905 — Signalunterbrechung führte zu Root-Ausführung.
  • CVE-2004-0794 — Remote-SIGURG-Signal nutzte Handler-Logik aus.
  • CVE-2004-2259 — SIGCHLD während malloc verursachte Abstürze.

Referenzen

  1. MITRE Corporation. "CWE-364: Signal Handler Race Condition." https://cwe.mitre.org/data/definitions/364.html
  2. Linux man-pages. "signal-safety(7)." https://man7.org/linux/man-pages/man7/signal-safety.7.html