Fehlende Freigabe von Ressource nach effektiver Lebensdauer

Beschreibung

Fehlende Freigabe von Ressource nach effektiver Lebensdauer ist eine Ressourcenverwaltungsschwachstelle, bei der Software eine Ressource nicht freigibt, nachdem sie nicht mehr benötigt wird. Dies betrifft verschiedene Ressourcentypen einschließlich Speicherallokationen, Datei-Handles, Datenbankverbindungen, Netzwerk-Sockets, Locks und andere Systemressourcen. Wenn Ressourcen nicht ordnungsgemäß freigegeben werden, bleiben sie unbegrenzt alloziert und verbrauchen allmählich verfügbare Ressourcen bis zur Erschöpfung.

Risiko

Die Nichtfreigabe von Ressourcen führt zu Ressourcenlecks, die sich im Laufe der Zeit ansammeln und schließlich Ressourcenerschöpfung verursachen. Dies kann zu Denial of Service führen, wenn der Anwendung oder dem System die verfügbaren Ressourcen ausgehen. Speicherlecks verursachen, dass Anwendungen zunehmende Speichermengen verbrauchen, bis sie abstürzen oder Out-of-Memory-Bedingungen auslösen. Dateideskriptor-Lecks verhindern das Öffnen neuer Dateien oder Netzwerkverbindungen. Datenbankverbindungs-Lecks erschöpfen Verbindungspools. Angreifer können die Ressourcenerschöpfung beschleunigen, indem sie wiederholt Codepfade auslösen, die ohne Freigabe allozieren.

Lösung

Stellen Sie sicher, dass jede allozierte Ressource freigegeben wird, wenn sie nicht mehr benötigt wird. Verwenden Sie RAII (Resource Acquisition Is Initialization) Muster in C++, wo Destruktoren automatisch Ressourcen freigeben. In Java und ähnlichen Sprachen verwenden Sie try-with-resources oder try-finally Muster, um Bereinigung zu garantieren. Schließen Sie Dateien, Sockets und Verbindungen explizit an allen Ausstiegspunkten einschließlich Fehlerpfaden. Verwenden Sie Garbage-Collected-Sprachen wo angemessen, aber denken Sie daran, dass GC nur Speicher behandelt - andere Ressourcen benötigen weiterhin explizite Freigabe.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch (Speicher) - Speicherlecks erschöpfen verfügbaren Speicher und verursachen Abstürze oder systemweite Auswirkungen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch (Ändere) - Dateideskriptor-, Socket- oder Verbindungslecks verhindern neue Allokationen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch (CPU) - Ressourcenverfolgungsaufwand steigt mit zunehmenden Lecks.

Beispielcode

Verwundbarer Code

// Verwundbar: Datei-Handle nie explizit geschlossen
private void processFile(String fName) throws IOException {
    BufferedReader fil = new BufferedReader(new FileReader(fName));
    String line;
    while ((line = fil.readLine()) != null) {
        processLine(line);
    }
    // Verwundbar: fil.close() nie aufgerufen
    // Dateideskriptor durchgesickert
}

// Verwundbar: Exception verhindert close
private void processFileWithException(String fName) throws IOException {
    BufferedReader fil = new BufferedReader(new FileReader(fName));
    String line;
    while ((line = fil.readLine()) != null) {
        processLine(line);  // Kann Exception werfen
    }
    fil.close();  // Nie erreicht wenn Exception geworfen wird
}
// Verwundbar: Datei-Handle nicht bei Fehlerpfad geschlossen
int decodeFile(char* fName) {
    int rc;
    FILE* f = fopen(fName, "r");
    if (!f) {
        return DECODE_FAIL;
    }

    rc = setRecordType(f);
    if (rc != SUCCESS) {
        return DECODE_FAIL;  // Verwundbar: f nicht geschlossen!
    }

    rc = processRecords(f);
    if (rc != SUCCESS) {
        return DECODE_FAIL;  // Verwundbar: f nicht geschlossen!
    }

    fclose(f);
    return DECODE_SUCCESS;
}

Gefixter Code

// Gefixt: try-with-resources verwenden (Java 7+)
private void processFile(String fName) throws IOException {
    try (BufferedReader fil = new BufferedReader(new FileReader(fName))) {
        String line;
        while ((line = fil.readLine()) != null) {
            processLine(line);
        }
    }  // Automatisch geschlossen, auch bei Exception
}

// Gefixt: try-finally verwenden (älteres Java)
private void processFileLegacy(String fName) throws IOException {
    BufferedReader fil = null;
    try {
        fil = new BufferedReader(new FileReader(fName));
        String line;
        while ((line = fil.readLine()) != null) {
            processLine(line);
        }
    } finally {
        if (fil != null) {
            fil.close();
        }
    }
}
// Gefixt: Datei auf allen Pfaden schließen
int decodeFile(char* fName) {
    int rc;
    int result = DECODE_SUCCESS;
    FILE* f = fopen(fName, "r");
    if (!f) {
        return DECODE_FAIL;
    }

    rc = setRecordType(f);
    if (rc != SUCCESS) {
        result = DECODE_FAIL;
        goto cleanup;
    }

    rc = processRecords(f);
    if (rc != SUCCESS) {
        result = DECODE_FAIL;
        goto cleanup;
    }

cleanup:
    fclose(f);  // Immer schließen
    return result;
}
# Gefixt: Context Manager verwenden
def query_database(query):
    with psycopg2.connect(database="mydb") as conn:
        with conn.cursor() as cursor:
            cursor.execute(query)
            results = cursor.fetchall()
    return results  # Verbindung automatisch geschlossen

# Gefixt: Datei mit Context Manager
def read_config(filename):
    with open(filename, 'r') as f:
        config = json.load(f)  # Datei auch bei Exception geschlossen
    return config

CVE-Beispiele

  • CVE-2007-0897: Antivirus-Produkt schloss Dateideskriptor nicht beim Auftreten von fehlerhaften Dateien, was zu Dateideskriptor-Erschöpfung und fehlgeschlagenen Scans führte.
  • CVE-2001-0830: Sockets blieben während wiederholter Verbindungs-/Trennungszyklen offen und erschöpften verfügbare Sockets.
  • CVE-2009-2054: Dateideskriptor-Erschöpfung durch Verarbeitung größer TCP-Paketvolumen.

Referenzen

  1. MITRE Corporation. "CWE-772: Missing Release of Resource after Effective Lifetime." https://cwe.mitre.org/data/definitions/772.html
  2. CERT C Coding Standard. "FIO42-C. Close files when they are no longer needed."
  3. C++ Core Guidelines. "R.1: Manage resources automatically using resource handles and RAII."