Verwendung abgelaufener Dateideskriptoren
Beschreibung
Verwendung abgelaufener Dateideskriptoren tritt auf, wenn Software einen Dateideskriptor nach dessen Schließung verwendet oder darauf zugreift. Sobald ein Dateideskriptor geschlossen wird, wird er an das Betriebssystem zurückgegeben und steht für die Neuzuweisung an neue Dateioperationen zur Verfügung. Wenn das Programm den geschlossenen Dateideskriptor weiterhin verwendet, kann es versehentlich von einer völlig anderen Datei, einem Gerät oder einem Socket lesen oder darauf schreiben, dem dieselbe Deskriptornummer zugewiesen wurde. Dies führt zu Datenkorruption, Informationsoffenlegung oder unerwartetem Programmverhalten.
Risiko
Die Verwendung abgelaufener Dateideskriptoren kann schwerwiegende Konsequenzen haben. Daten, die für eine Datei bestimmt sind, können in eine andere Datei geschrieben werden, was Korruption oder Informationslecks verursacht. Das Programm kann Daten aus einer unbeabsichtigten Quelle lesen, was zu falscher Verarbeitung führt. Netzwerk-Sockets können verwechselt werden, was dazu führt, dass Daten an falsche Empfänger gesendet werden. Kritische Dateien können beschädigt werden, wenn Schreibvorgänge an unbeabsichtigte Ziele gehen. Programme können abstürzen, wenn Operationen auf ungültigen Deskriptoren versucht werden. In Multithread-Anwendungen erhöhen Race Conditions rund um die Wiederverwendung von Dateideskriptoren die Wahrscheinlichkeit und Schwere dieses Problems.
Lösung
Verfolgen Sie den Lebenszyklus von Dateideskriptoren sorgfältig und stellen Sie sicher, dass Deskriptoren nach dem Schließen nicht verwendet werden. Setzen Sie Dateideskriptoren nach dem Schließen auf -1, um versehentliche Wiederverwendung zu verhindern. Verwenden Sie Wrapper-Strukturen, die den geöffnet/geschlossen-Status verfolgen. Implementieren Sie defensive Prüfungen vor Dateioperationen. In Multithread-Code verwenden Sie ordnungsgemäße Synchronisation beim Zugriff auf gemeinsam genutzte Dateideskriptoren. Erwägen Sie die Verwendung von Abstraktionen auf höherer Ebene (Streams, Dateiobjekte), die den Deskriptor-Lebenszyklus automatisch verwalten. Verwenden Sie statische Analysetools, um Use-after-close-Muster zu erkennen. In Sprachen mit Ressourcenverwaltung verwenden Sie RAII-Muster oder try-with-resources, um ordnungsgemäße Bereinigung sicherzustellen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Programm kann Daten aus falscher Datei lesen, wenn Deskriptor neu zugewiesen wurde. |
| Integrität | Bereich: Integrität Anwendungsdaten ändern - Schreibvorgänge auf geschlossenen Deskriptor können unbeabsichtigte Dateien beschädigen oder Daten an falsche Empfänger senden. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz/Beenden/Neustart - Zugriff auf geschlossenen Dateideskriptor kann Abstürze oder undefiniertes Verhalten verursachen. |
Beispielcode + Lösungscode
Verwundbarer Code
// Verwundbar: Verwendung von Dateideskriptor nach close
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
void vulnerable_file_operation() {
int fd = open("/tmp/data.txt", O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open");
return;
}
write(fd, "Initiale Daten\n", 15);
// Datei schließen
close(fd);
// ... anderer Code, der neue Dateien öffnen könnte ...
// fd-Wert (z.B. 3) könnte neuer Datei zugewiesen werden
// Verwundbar: Verwendung von fd nach close
// Kann in falsche Datei schreiben!
write(fd, "Mehr Daten\n", 11);
}
// Verwundbar: Doppeltes Close und Verwendung
void vulnerable_double_close(int fd) {
// Erstes Close
close(fd);
// ... etwas Code ...
// Verwundbar: fd könnte zwischen Closes neu zugewiesen werden
close(fd); // Kann eine andere Datei schließen!
}
Sicherer Code
// Behoben: Deskriptor-Status verfolgen und nach Close leeren
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
void fixed_file_operation() {
int fd = open("/tmp/data.txt", O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open");
return;
}
write(fd, "Initiale Daten\n", 15);
// Schließen und Deskriptor invalidieren
close(fd);
fd = -1; // Behoben: Als ungültig markieren
// ... anderer Code ...
// Behoben: Vor Verwendung prüfen
if (fd >= 0) {
write(fd, "Mehr Daten\n", 11); // Wird nicht ausgeführt
}
}
// Hilfsfunktion für sicheres Close
int safe_close(int *fd) {
if (fd == NULL || *fd < 0) {
return 0; // Bereits geschlossen oder ungültig
}
int result = close(*fd);
*fd = -1; // Als geschlossen markieren
return result;
}
// Behoben: Ordnungsgemäße Fehlerbehandlung mit einzelnem Close-Punkt
int fixed_process_file(const char *path) {
int fd = -1;
int result = -1;
char buffer[1024];
fd = open(path, O_RDONLY);
if (fd < 0) {
goto cleanup;
}
if (read(fd, buffer, sizeof(buffer)) < 0) {
goto cleanup;
}
if (validate_data(buffer) < 0) {
goto cleanup;
}
if (read(fd, buffer, sizeof(buffer)) < 0) {
goto cleanup;
}
result = 0; // Erfolg
cleanup:
// Behoben: Einzelner Close-Punkt, Deskriptor immer prüfen
if (fd >= 0) {
close(fd);
}
return result;
}
# Behoben: Kontextmanager für automatische Bereinigung verwenden
def fixed_process_file(filename):
# Behoben: Kontextmanager stellt ordnungsgemäßes Close sicher
with open(filename, 'r') as f:
data = f.read()
if needs_more_data(data):
# Datei noch offen innerhalb des Kontexts
more_data = f.read()
# Datei hier automatisch geschlossen
Verwandte CWEs
- CWE-672: Operation auf einer Ressource nach Ablauf oder Freigabe (Eltern)
- CWE-416: Use After Free (verwandt - Speicher-Analogon)
- CWE-775: Fehlende Freigabe von Dateideskriptor oder Handle nach effektiver Lebensdauer (verwandt)
- CWE-399: Ressourcenverwaltungsfehler (Kategorie)
Referenzen
- MITRE Corporation. "CWE-910: Use of Expired File Descriptor." https://cwe.mitre.org/data/definitions/910.html
- CERT C Secure Coding Standard. "FIO46-C. Do not access a closed file."
- CERT C Secure Coding Standard. "FIO42-C. Close files when they are no longer needed."