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
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Ressourcenverbrauch (Speicher) - Speicherlecks erschöpfen verfügbaren Speicher und verursachen Abstürze oder systemweite Auswirkungen. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Ressourcenverbrauch (Ändere) - Dateideskriptor-, Socket- oder Verbindungslecks verhindern neue Allokationen. |
| Verfügbarkeit | Bereich: 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
- MITRE Corporation. "CWE-772: Missing Release of Resource after Effective Lifetime." https://cwe.mitre.org/data/definitions/772.html
- CERT C Coding Standard. "FIO42-C. Close files when they are no longer needed."
- C++ Core Guidelines. "R.1: Manage resources automatically using resource handles and RAII."