Rückgabe eines Zeigerwertes außerhalb des erwarteten Bereichs
Beschreibung
Rückgabe eines Zeigerwertes außerhalb des erwarteten Bereichs ist eine Schwachstelle, bei der eine Funktion einen Pointer auf Speicher zurückgeben kann, der außerhalb des Puffers liegt, auf den der Pointer verweisen sollte. Wenn Funktionen Pointer zurückgeben, die auf Speicherorte jenseits der beabsichtigten Puffergrenzen verweisen, können nachfolgende Operationen mit diesen Zeigern unbeabsichtigte Speicherorte lesen oder beschreiben, was zu Informationspreisgabe, Datenkorruption oder Code-Ausführung führt.
Risiko
Die Rückgabe von Zeigern außerhalb des Bereichs erzeugt schwerwiegende Speichersicherheits-Schwachstellen. Das Lesen durch solche Pointer kann sensible Daten aus benachbartem Speicher preisgeben. Das Schreiben durch sie kann kritische Datenstrukturen korrumpieren oder Code-Pointer überschreiben, um beliebige Code-Ausführung zu erreichen. Die unvorhersehbare Natur, welche Speicherorte betroffen sind, macht das Debuggen schwierig und die Ausnutzung potenziell zuverlässig. In sicherheitskritischen Anwendungen können diese Schwachstellen die Speicherisolierung und Zugriffskontrollen vollständig untergraben.
Lösung
Implementieren Sie rigorose Grenzprüfung bei allen Zeigerarithmetik- und -berechnungen vor der Rückgabe von Zeigern. Verwenden Sie statische Analysewerkzeuge (SAST), um Quellcode auf anfällige Datenflussmuster zu analysieren. Setzen Sie dynamische Analysewerkzeuge wie AddressSanitizer (ASan) ein, um Out-of-Bounds-Speicherzugriffe zur Laufzeit zu erkennen. Erwägen Sie die Verwendung sichererer Abstraktionen wie Array-Slices oder Span-Typen, die Längeninformationen mitführen. Validieren Sie alle Indizes und Offsets vor der Berechnung von Zeigerwerten. Geben Sie Fehlercodes oder NULL zurück anstelle ungültiger Pointer, wenn Grenzverletzungen erkannt werden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Umfang: Vertraulichkeit Speicher lesen - Pointer außerhalb des Bereichs ermöglichen das Lesen von Speicher außerhalb beabsichtigter Puffergrenzen, was möglicherweise sensible Daten preisgibt. |
| Integrität | Umfang: Integrität Speicher modifizieren - Das Schreiben durch Pointer außerhalb des Bereichs kann beliebige Speicherorte modifizieren, was Daten oder Kontrollstrukturen korrumpiert. |
Beispielcode
Anfälliger Code
// Anfällig: Zeigerberechnung kann außerhalb der Grenzen gehen
#include <stdio.h>
#include <stdlib.h>
char* vulnerable_get_element(char* array, size_t array_size, int index) {
// Anfällig: Keine Grenzprüfung
// index könnte negativ sein oder >= array_size
return &array[index];
// Wenn index -1 ist, wird Pointer vor Array zurückgegeben
// Wenn index array_size ist, wird Pointer nach Array zurückgegeben
}
// Anfällig: Integer-Überlauf bei Zeigerberechnung
char* vulnerable_offset_pointer(char* base, size_t offset) {
// Anfällig: offset * sizeof(element) könnte überlaufen
// Resultat ist Pointer, der zurück umwickelt
return base + offset;
// Großer Offset könnte Pointer zurück in gültig aussehenden
// aber unbeabsichtigten Speicher wickeln
}
// Anfällig: Pointer hinter Pufferende zurückgeben
typedef struct {
char data[256];
int length;
} Buffer;
char* vulnerable_find_end(Buffer* buf) {
// Anfällig: Gibt Pointer hinter gültigen Daten zurück
return buf->data + buf->length;
// Wenn length > 256, gibt Pointer in benachbarten Speicher zurück
// Selbst wenn length == 256, ist Pointer eins nach Ende
}
// Anfällig: Suchfunktion gibt ungültigen Pointer zurück
char* vulnerable_find_char(char* str, size_t len, char target) {
for (size_t i = 0; i <= len; i++) { // Anfällig: <= anstelle von <
if (str[i] == target) {
return &str[i];
}
}
// Anfällig: Gibt Pointer hinter Ende bei Nicht-Gefunden zurück
return &str[len]; // Eins nach Ende, ungültig für Dereferenzierung
}
// Anfällig: Array-Zugriff mit berechnetem Index
int* vulnerable_matrix_element(int* matrix, int rows, int cols, int row, int col) {
// Anfällig: Keine Validierung von row und col
int index = row * cols + col;
// Wenn row oder col außerhalb des Bereichs ist, ist Index falsch
// Wenn row * cols überläuft, wickelt Index herum
return &matrix[index];
}
// Anfällig: String-Manipulation gibt Out-of-Bounds zurück
char* vulnerable_skip_prefix(char* str, const char* prefix) {
size_t prefix_len = strlen(prefix);
size_t str_len = strlen(str);
// Anfällig: Verifiziert nicht dass Präfix existiert
// Anfällig: Verifiziert nicht dass str lang genug ist
return str + prefix_len;
// Wenn prefix_len > str_len, gibt Pointer hinter str zurück
}
// Anfällig: Iterator gibt Past-End-Pointer zurück
class VulnerableContainer {
int* data;
size_t size;
public:
// Anfällig: Keine Grenzprüfung
int* at(size_t index) {
return &data[index]; // Könnte hinter Ende sein
}
// Anfällig: begin/end ohne ordnungsgemäße Validierung
int* begin() { return data; }
int* end() { return data + size; } // Eins nach Ende - Vorsicht!
// Anfällig: Gibt Pointer zurück, der ungültig sein könnte
int* find(int value) {
for (size_t i = 0; i < size; i++) {
if (data[i] == value) {
return &data[i];
}
}
return end(); // Past-End, aber häufig verwendetes Muster
// Gefährlich wenn Aufrufer ohne Prüfung dereferenziert
}
};
Korrigierter Code
// Korrigiert: Ordnungsgemäße Grenzprüfung
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
// Korrigiert: NULL für ungültige Indizes zurückgeben
char* secure_get_element(char* array, size_t array_size, size_t index) {
// Korrigiert: Grenzen validieren
if (array == NULL || index >= array_size) {
return NULL;
}
return &array[index];
}
// Korrigiert: Auf Überlauf bei Zeigerberechnung prüfen
char* secure_offset_pointer(char* base, size_t base_size, size_t offset) {
// Korrigiert: Validieren dass offset innerhalb der Grenzen ist
if (base == NULL || offset >= base_size) {
return NULL;
}
// Korrigiert: Auf Zeigerarithmetik-Überlauf prüfen
uintptr_t base_addr = (uintptr_t)base;
if (offset > SIZE_MAX - base_addr) {
return NULL; // Würde überlaufen
}
return base + offset;
}
// Korrigiert: Pufferlange validieren
typedef struct {
char data[256];
size_t length; // Geändert zu size_t
} SafeBuffer;
// Korrigiert: Informationen über Gültigkeit zurückgeben
typedef struct {
char* ptr;
int valid;
} PointerResult;
PointerResult secure_find_end(SafeBuffer* buf) {
PointerResult result = {NULL, 0};
if (buf == NULL) {
return result;
}
// Korrigiert: Validieren dass Länge innerhalb der Grenzen ist
if (buf->length > sizeof(buf->data)) {
return result; // Ungültige Länge
}
// Korrigiert: Letztes gültiges Zeichen zurückgeben, nicht eins-nach-Ende
if (buf->length > 0) {
result.ptr = &buf->data[buf->length - 1];
result.valid = 1;
}
return result;
}
// Korrigiert: Suchfunktion mit ordnungsgemäßem Rückgabewert
// Gibt Index anstatt Pointer zurück, -1 für nicht gefunden
ssize_t secure_find_char(const char* str, size_t len, char target) {
if (str == NULL) {
return -1;
}
for (size_t i = 0; i < len; i++) { // Korrigiert: < anstelle von <=
if (str[i] == target) {
return (ssize_t)i;
}
}
return -1; // Korrigiert: Sentinel-Wert zurückgeben, nicht ungültiger Pointer
}
// Korrigiert: Sichere Matrix-Zugriff
typedef struct {
int* data;
int valid;
} ElementResult;
ElementResult secure_matrix_element(int* matrix, size_t rows, size_t cols,
size_t row, size_t col) {
ElementResult result = {NULL, 0};
// Korrigiert: Alle Grenzen validieren
if (matrix == NULL || row >= rows || col >= cols) {
return result;
}
// Korrigiert: Auf Multiplikationsüberlauf prüfen
if (row > SIZE_MAX / cols) {
return result;
}
size_t index = row * cols + col;
// Korrigiert: Verifizieren dass Gesamtindex gültig ist
if (index >= rows * cols) {
return result;
}
result.data = &matrix[index];
result.valid = 1;
return result;
}
// Korrigiert: String-Manipulation mit Validierung
const char* secure_skip_prefix(const char* str, const char* prefix) {
if (str == NULL || prefix == NULL) {
return NULL;
}
size_t prefix_len = strlen(prefix);
size_t str_len = strlen(str);
// Korrigiert: Verifizieren dass String das Präfix hat
if (str_len < prefix_len) {
return NULL; // String kürzer als Präfix
}
if (strncmp(str, prefix, prefix_len) != 0) {
return NULL; // Präfix stimmt nicht überein
}
// Korrigiert: Sicher Offset-Pointer zurückzugeben
return str + prefix_len;
}
// Korrigiert: Sichere Container mit Grenzprüfung
#include <optional>
#include <stdexcept>
class SafeContainer {
int* data;
size_t capacity;
size_t count;
public:
// Korrigiert: Gibt optional zurück um Gültigkeit anzuzeigen
std::optional<int*> at(size_t index) {
if (index >= count) {
return std::nullopt;
}
return &data[index];
}
// Korrigiert: Werfende Version wenn Exception angemessen ist
int& at_checked(size_t index) {
if (index >= count) {
throw std::out_of_range("Index außerhalb der Grenzen");
}
return data[index];
}
// Korrigiert: Iterator-Schnittstelle ist sicher wenn korrekt verwendet
// aber Vertrag dokumentieren
class iterator {
int* ptr;
int* end;
public:
iterator(int* p, int* e) : ptr(p), end(e) {}
// Korrigiert: Vor Dereferenzierung prüfen
int& operator*() {
if (ptr >= end) {
throw std::out_of_range("Dereferenzierung von Past-End-Iterator");
}
return *ptr;
}
bool valid() const { return ptr < end; }
};
// Korrigiert: find gibt optional zurück
std::optional<size_t> find(int value) {
for (size_t i = 0; i < count; i++) {
if (data[i] == value) {
return i; // Index zurückgeben, nicht Pointer
}
}
return std::nullopt;
}
};
CVE-Beispiele
Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgeführt. Das Muster ist jedoch verwandt mit:
- Puffer-Überlesungs-Schwachstellen (CWE-125)
- Out-of-Bounds-Schreib-Schwachstellen (CWE-787)
- Seven Pernicious Kingdoms: "Illegal Pointer Value"
Referenzen
- MITRE Corporation. "CWE-466: Return of Pointer Value Outside of Expected Range." https://cwe.mitre.org/data/definitions/466.html
- CERT C Secure Coding Standard. "ARR30-C. Do not form or use out-of-bounds pointers or array subscripts."
- Google. "AddressSanitizer: A Fast Address Sanity Checker."