Zugriff auf nicht initialisierten Pointer
Beschreibung
Zugriff auf nicht initialisierten Pointer ist eine Speichersicherheits-Schwachstelle, bei der Software auf eine Zeigervariable zugreift oder sie verwendet, die nicht mit einer gültigen Speicheradresse initialisiert wurde. Nicht initialisierte Pointer enthalten unbestimmte Werte - welche Daten auch immer an diesem Speicherort zufällig vorhanden waren. Wenn sie dereferenziert werden, können solche Pointer auf beliebige Speicherorte verweisen, was das Programm dazu bringt, von unerwarteten Speicherbereichen zu lesen oder in sie zu schreiben. Wenn der Pointer als Funktionszeiger verwendet wird, könnten beliebige Funktionen aufgerufen werden. Die Schwere dieser Schwachstelle hängt vom Speicher-Layout, Speicherverwaltungsverhalten und wie das Produkt arbeitet ab.
Risiko
Diese Schwachstelle kann zu Abstürzen, Informationsoffenlegung oder Codeausführung führen. Wenn ein nicht initialisierter Pointer zum Lesen dereferenziert wird, kann er auf sensible Daten aus anderen Teilen des Speichers zugreifen, oder er kann auf ungültigen Speicher zugreifen und einen Absturz verursachen. Wenn er zum Schreiben verwendet wird, kann er beliebige Speicherorte beschädigen und potenziell sicherheitskritische Daten oder Funktionszeiger überschreiben. Angreifer, die den Inhalt von nicht initialisiertem Speicher beeinflussen können (durch Heap-Spraying oder andere Techniken), können kontrollieren, wohin der Pointer zeigt, und die Ausnutzung für beliebige Codeausführung ermöglichen. Die unvorhersehbare Natur nicht initialisierter Daten macht diese Schwachstelle in sicherheitssensitiven Kontexten besonders gefährlich.
Lösung
Initialisieren Sie Pointer immer bei ihrer Deklaration, entweder mit einer gültigen Adresse oder mit NULL. Verwenden Sie Compiler-Warnungen (-Wuninitialized, -Werror), um die Verwendung nicht initialisierter Variablen zu erkennen. Initialisieren Sie alle lokalen Variablen bei der Deklaration, besonders in C/C++. Verwenden Sie statische Analysetools, um potenzielle Verwendung nicht initialisierter Pointer zu erkennen. Bevorzugen Sie in C++ RAII-Muster und Smart Pointer, die Initialisierung erzwingen. Erwägen Sie die Verwendung von Sprachen mit automatischer Speicherverwaltung, wo der Zugriff auf nicht initialisierte Pointer nicht möglich ist. Wenn ein Pointer möglicherweise nicht in allen Codepfaden zugewiesen wird, initialisieren Sie ihn mit NULL und prüfen Sie vor der Dereferenzierung. Aktivieren Sie während des Testens Laufzeit-Sanitizer wie MemorySanitizer, um den Zugriff auf nicht initialisierten Speicher zu erkennen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Speicher lesen - Nicht initialisierte Pointer, die in Leseoperationen verwendet werden, könnten sensible Speicherinhalte von beliebigen Orten preisgeben. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz, Beendigung oder Neustart - Ungültige Speicherreferenzen verursachen Abstürze beim Zugriff auf unzugänglichen oder fehlerhaften Speicher. |
| Integrität, Vertraulichkeit, Verfügbarkeit | Bereich: Integrität, Vertraulichkeit, Verfügbarkeit Unerlaubten Code oder Befehle ausführen - Wenn als Funktionszeiger oder für Schreibvorgänge verwendet, können Angreifer möglicherweise beliebige Codeausführung erreichen. |
Beispielcode
Anfälliger Code
// Anfällig: Pointer deklariert aber nicht initialisiert
void vulnerable_uninitialized(int condition) {
char *ptr; // Nicht initialisiert - enthält Müllwert
if (condition) {
ptr = malloc(100);
strcpy(ptr, "daten");
}
// Anfällig: ptr kann nicht initialisiert sein, wenn condition falsch ist
printf("Wert: %s\n", ptr); // Dereferenziert Mülladresse
}
// Anfällig: Pointer nicht in allen Codepfaden initialisiert
int vulnerable_conditional(int type, char **out_ptr) {
char *buffer; // Nicht initialisiert
if (type == 1) {
buffer = malloc(256);
strcpy(buffer, "Typ 1 Daten");
} else if (type == 2) {
buffer = malloc(512);
strcpy(buffer, "Typ 2 Daten");
}
// Anfällig: wenn type != 1 und type != 2, ist buffer nicht initialisiert
*out_ptr = buffer; // Kann Müllzeiger speichern
return 0;
}
// Anfällig: Funktionszeiger nicht initialisiert
typedef void (*callback_t)(void);
void vulnerable_callback(int registered) {
callback_t handler; // Nicht initialisierter Funktionszeiger
if (registered) {
handler = registered_callback;
}
// Anfällig: Wenn nicht registriert, ist handler Müll
handler(); // Ruft beliebige Adresse auf!
}
Korrigierter Code
// Korrigiert: Pointer bei Deklaration initialisieren
void fixed_uninitialized(int condition) {
char *ptr = NULL; // Korrigiert: Mit NULL initialisiert
if (condition) {
ptr = malloc(100);
if (ptr) {
strcpy(ptr, "daten");
}
}
// Korrigiert: Vor Dereferenzierung prüfen
if (ptr != NULL) {
printf("Wert: %s\n", ptr);
free(ptr);
}
}
// Korrigiert: Initialisieren und alle Codepfade behandeln
int fixed_conditional(int type, char **out_ptr) {
char *buffer = NULL; // Korrigiert: Mit NULL initialisieren
if (type == 1) {
buffer = malloc(256);
if (buffer) strcpy(buffer, "Typ 1 Daten");
} else if (type == 2) {
buffer = malloc(512);
if (buffer) strcpy(buffer, "Typ 2 Daten");
} else {
// Korrigiert: Standardfall behandeln
*out_ptr = NULL;
return -1; // Unbekannter Typ
}
*out_ptr = buffer;
return buffer ? 0 : -1;
}
// Korrigiert: Funktionszeiger initialisieren
typedef void (*callback_t)(void);
static void default_handler(void) {
// No-op Standard-Handler
}
void fixed_callback(int registered) {
callback_t handler = default_handler; // Korrigiert: Sicherer Standard
if (registered) {
handler = registered_callback;
}
// Sicher: handler zeigt immer auf gültige Funktion
handler();
}
// Korrigiert: Alle Struct-Mitglieder initialisieren
struct node {
int value;
struct node *next;
};
void fixed_list(void) {
struct node head = {0}; // Korrigiert: Alle Mitglieder mit Null initialisieren
// Oder explizit:
// struct node head;
// head.value = 1;
// head.next = NULL;
head.value = 1;
struct node *current = &head;
while (current != NULL) {
printf("%d\n", current->value);
current = current->next; // Sicher: next ist NULL
}
}
Verwandte CWEs
- CWE-119: Unzureichende Beschränkung von Operationen innerhalb der Grenzen eines Speicherpuffers (Eltern)
- CWE-457: Verwendung nicht initialisierter Variablen (verwandt - allgemeine Form)
- CWE-125: Out-of-bounds-Lesezugriff (kann folgen)
- CWE-787: Out-of-bounds-Schreibzugriff (kann folgen)
- CWE-908: Verwendung nicht initialisierter Ressourcen (Eltern)
Referenzen
- MITRE Corporation. "CWE-824: Access of Uninitialized Pointer." https://cwe.mitre.org/data/definitions/824.html
- CERT C Secure Coding Standard. "EXP33-C. Do not read uninitialized memory."
- Google. "MemorySanitizer." https://github.com/google/sanitizers/wiki/MemorySanitizer