Fehlende Referenz auf aktiven Dateideskriptor oder Handle

Beschreibung

Fehlende Referenz auf aktiven Dateideskriptor oder Handle ist eine Ressourcenverwaltungsschwachstelle, bei der Software keine ordnungsgemäßen Referenzen auf Dateideskriptoren oder Handles behält, was deren Rückgewinnung verhindert. Dies tritt auf, wenn ein Dateideskriptor geöffnet wird, aber die Variable, die ihn hält, überschrieben wird, den Gültigkeitsbereich verlässt oder anderweitig verloren geht, bevor der Deskriptor geschlossen wird. Der verwaiste Dateideskriptor kann nicht geschlossen werden, da das Programm keine Möglichkeit mehr hat, ihn zu referenzieren.

Risiko

Dateideskriptoren sind eine begrenzte Systemressource. Wenn Referenzen auf aktive Dateideskriptoren verloren gehen, werden sie verwaist und verbrauchen unbegrenzt Ressourcen. Wiederholtes Auftreten erschöpft den verfügbaren Dateideskriptor-Pool und verursacht Denial of Service. Anwendungen können keine neuen Dateien öffnen, keine Netzwerkverbindungen herstellen oder keine I/O-Operationen durchführen. Da Dateideskriptor-Limits typischerweise systemweit oder pro Prozess sind, kann Erschöpfung andere Anwendungen auf demselben System beeinträchtigen.

Lösung

Behalten Sie immer Referenzen auf Dateideskriptoren, bis sie explizit geschlossen werden. Überschreiben Sie niemals eine Dateideskriptor-Variable, ohne zuerst den bestehenden Deskriptor zu schließen. Verwenden Sie RAII-Muster in C++ oder try-with-resources in Java, um die Deskriptor-Lebensdauer an den Bereich zu binden. Implementieren Sie Wrapper-Klassen, die den Deskriptor-Zustand verfolgen und Bereinigung sicherstellen. Verwenden Sie Ressourcenpools, die aktive Deskriptoren verfolgen. Setzen Sie Dateideskriptor-Limits auf Prozessebene mit setrlimit, um Schäden durch Lecks zu begrenzen.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Dateideskriptor-Erschöpfung verhindert das Öffnen von Dateien, Sockets und anderen I/O-Operationen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Instabilität - System wird instabil bei Dateideskriptor-Erschöpfung.

Beispielcode

Verwundbarer Code

// Verwundbar: Dateideskriptor-Referenz verloren
void vulnerable_fd_overwrite() {
    int fd = open("file1.txt", O_RDONLY);
    if (fd < 0) return;

    // ... fd verwenden ...

    // Verwundbar: fd ohne Schließen überschrieben
    fd = open("file2.txt", O_RDONLY);  // Ursprünglicher fd durchgesickert!
    if (fd < 0) return;

    // ... fd verwenden ...

    close(fd);  // Schließt nur file2.txt
}

// Verwundbar: Referenz in Schleife verloren
void vulnerable_loop() {
    int fd;
    for (int i = 0; i < 100; i++) {
        char filename[64];
        snprintf(filename, sizeof(filename), "file%d.txt", i);

        // Verwundbar: Vorheriger fd bei jeder Iteration verloren
        fd = open(filename, O_RDONLY);
        if (fd < 0) continue;

        processFile(fd);
        // Kein close vor nächster Iteration
    }
    close(fd);  // Schließt nur letzte Datei
}
// Verwundbar: Deskriptor bei Fehlerpfad verloren
int vulnerable_error_path(const char* filename) {
    int fd = open(filename, O_RDONLY);
    if (fd < 0) return -1;

    char* buffer = malloc(4096);
    if (buffer == NULL) {
        // Verwundbar: fd-Referenz verloren, Deskriptor durchgesickert
        return -1;
    }

    ssize_t bytes = read(fd, buffer, 4096);
    if (bytes < 0) {
        free(buffer);
        // Verwundbar: fd nicht geschlossen
        return -1;
    }

    free(buffer);
    close(fd);
    return 0;
}

Gefixter Code

// Gefixt: Vor Neuzuweisung schließen
void fixed_fd_overwrite() {
    int fd = open("file1.txt", O_RDONLY);
    if (fd < 0) return;

    // ... fd verwenden ...

    close(fd);  // Gefixt: Vor Neuzuweisung schließen

    fd = open("file2.txt", O_RDONLY);
    if (fd < 0) return;

    // ... fd verwenden ...

    close(fd);
}

// Gefixt: In jeder Iteration schließen
void fixed_loop() {
    for (int i = 0; i < 100; i++) {
        char filename[64];
        snprintf(filename, sizeof(filename), "file%d.txt", i);

        int fd = open(filename, O_RDONLY);
        if (fd < 0) continue;

        processFile(fd);
        close(fd);  // Gefixt: Jede Datei schließen
    }
}
// Gefixt: Auf allen Fehlerpfaden schließen
int fixed_error_path(const char* filename) {
    int fd = open(filename, O_RDONLY);
    if (fd < 0) return -1;

    char* buffer = malloc(4096);
    if (buffer == NULL) {
        close(fd);  // Gefixt: Bei Fehler schließen
        return -1;
    }

    ssize_t bytes = read(fd, buffer, 4096);
    if (bytes < 0) {
        free(buffer);
        close(fd);  // Gefixt: Bei Fehler schließen
        return -1;
    }

    free(buffer);
    close(fd);
    return 0;
}
// Gefixt: RAII-Wrapper für Dateideskriptoren
#include <unistd.h>
#include <fcntl.h>

class FileDescriptor {
    int fd;

public:
    explicit FileDescriptor(const char* filename, int flags = O_RDONLY)
        : fd(open(filename, flags)) {}

    ~FileDescriptor() {
        if (fd >= 0) close(fd);
    }

    int get() const { return fd; }
    bool isValid() const { return fd >= 0; }

    // Move-Semantik für Eigentumsübergang
    FileDescriptor(FileDescriptor&& other) noexcept : fd(other.fd) {
        other.fd = -1;
    }

    FileDescriptor& operator=(FileDescriptor&& other) noexcept {
        if (this != &other) {
            if (fd >= 0) close(fd);
            fd = other.fd;
            other.fd = -1;
        }
        return *this;
    }

    // Kopieren verhindern
    FileDescriptor(const FileDescriptor&) = delete;
    FileDescriptor& operator=(const FileDescriptor&) = delete;
};

Erkennungsmethoden

  • Statische Analyse: SAST-Tools können Codepfade identifizieren, in denen Dateideskriptor-Variablen ohne Schließen neu zugewiesen werden.
  • Laufzeitanalyse: Tools wie lsof, /proc/[pid]/fd-Überwachung oder benutzerdefiniertes Tracking können Deskriptor-Lecks erkennen.
  • Ressourcenüberwachung: Dateideskriptor-Anzahl über Zeit verfolgen, um allmähliche Lecks zu identifizieren.

Referenzen

  1. MITRE Corporation. "CWE-773: Missing Reference to Active File Descriptor or Handle." https://cwe.mitre.org/data/definitions/773.html
  2. CERT C Coding Standard. "FIO42-C. Close files when they are no longer needed."
  3. Linux Manual. "getrlimit, setrlimit - get/set resource limits."