Speicherallokation mit übermäßigem Größenwert

Beschreibung

Speicherallokation mit übermäßigem Größenwert ist eine Ressourcenverwaltungsschwachstelle, bei der Software Speicher basierend auf einem nicht vertrauenswürdigen oder ungeprüften Größenwert alloziert, ohne sicherzustellen, dass die Größe innerhalb akzeptabler Grenzen liegt. Wenn Anwendungen Größenparameter aus externen Quellen (Benutzereingabe, Netzwerkdaten, Dateiinhalt) akzeptieren und sie direkt in Allokationsfunktionen wie malloc(), calloc() oder new verwenden, können Angreifer extrem große Werte angeben. Dies kann sofortige Allokationsfehler, Out-of-Memory-Bedingungen oder systemweite Ressourcenerschöpfung verursachen, die zu Denial of Service führt.

Risiko

Übermäßige Speicherallokation hat mehrere Angriffsvektoren. Direkte Angriffe spezifizieren riesige Allokationsgrößen, um die Anwendung mit Out-of-Memory-Fehlern abstürzen zu lassen oder Systemspeicher zu erschöpfen, der andere Prozesse betrifft. Integer-Overflow-Angriffe verursachen, dass Größenberechnungen auf kleine Werte umbrechen, was zu unterdimensionierten Puffern führt, die zu Heap Overflows führen, wenn sie mit tatsächlichen Daten gefüllt werden. Größe Allokationen können auch Leistungsverschlechterung auslösen, wenn das System Speicher auf die Festplatte auslagert.

Lösung

Validieren Sie immer Allokations-Größenparameter vor der Verwendung. Definieren und erzwingen Sie maximal akzeptable Größen basierend auf Anwendungsanforderungen. Prüfen Sie auf Integer Overflow in Größenberechnungen vor der Allokation. Verwenden Sie sichere Integer-Arithmetik-Funktionen oder manuelle Overflow-Prüfungen. Behandeln Sie Allokationsfehler elegant - nehmen Sie nicht an, dass malloc immer erfolgreich ist. Verwenden Sie Ressourcenlimits (ulimit, setrlimit), um Schäden durch unkontrollierte Allokationen zu begrenzen. Erwägen Sie die Verwendung von Speicherpools mit festen Maximalgrößen.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch (Speicher) - Übermäßige Allokationen erschöpfen verfügbaren Speicher und bringen Anwendung oder System zum Absturz.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz - Out-of-Memory-Bedingungen bringen typischerweise die Anwendung zum Absturz.
IntegritätBereich: Integrität

Speicher ändern - Integer Overflow in Größenberechnung kann zu unterdimensionierter Allokation und anschließendem Buffer Overflow führen.

Beispielcode

Verwundbarer Code

// Verwundbar: Ungeprüfter Allokationsgrößenwert aus Benutzereingabe
#include <stdlib.h>
#include <string.h>

void vulnerable_alloc_user_size(unsigned int size) {
    // Verwundbar: Keine Validierung von size
    char* buffer = (char*)malloc(size);
    if (buffer == NULL) {
        // Fehler behandeln, aber Schäden ist möglicherweise schon angerichtet
        return;
    }

    // Puffer verwenden...
    free(buffer);
}

// Angriff: size = 4294967295 (0xFFFFFFFF) übergeben um 4GB zu allozieren
// Verwundbar: Integer Overflow in Größenberechnung
void vulnerable_alloc_array(unsigned int count) {
    // Verwundbar: Multiplikation kann überlaufen
    unsigned int totalBytes = count * sizeof(int);

    // Wenn count = 1073741824 (0x40000000), umbrechen totalBytes zu 0
    int* buffer = (int*)malloc(totalBytes);
    if (buffer == NULL) return;

    // Schreibt weit über tatsächliche (kleine) Allokation hinaus
    for (unsigned int i = 0; i < count; i++) {
        buffer[i] = 0;  // Heap Overflow!
    }

    free(buffer);
}
// Verwundbar: Java HashMap mit nicht vertrauenswürdiger Kapazität
import java.util.HashMap;

public class VulnerableHashMap {
    public HashMap<String, String> createFromInput(int capacity) {
        // Verwundbar: Keine Validierung von capacity
        // Riesige Kapazität verursacht OutOfMemoryError
        HashMap<String, String> map = new HashMap<>(capacity);
        return map;
    }
}

// Angriff: capacity = Integer.MAX_VALUE übergeben

Gefixter Code

// Gefixt: Allokationsgröße validieren
#include <stdlib.h>
#include <stdint.h>

#define MAX_ALLOC_SIZE (64 * 1024 * 1024)  // 64 MB Maximum

void* fixed_alloc_user_size(size_t size) {
    // Gefixt: Größe gegen Maximum validieren
    if (size == 0 || size > MAX_ALLOC_SIZE) {
        return NULL;  // Ungültige Größen ablehnen
    }

    void* buffer = malloc(size);
    return buffer;  // Kann bei Allokationsfehler weiterhin NULL sein
}
// Gefixt: Auf Integer Overflow prüfen
#include <stdint.h>

void* fixed_alloc_array(size_t count, size_t element_size) {
    // Gefixt: Auf Overflow vor Multiplikation prüfen
    if (count == 0 || element_size == 0) {
        return NULL;
    }

    if (count > SIZE_MAX / element_size) {
        // Multiplikation würde überlaufen
        return NULL;
    }

    size_t total_bytes = count * element_size;

    // Zusätzliche Plausibilitätsprüfung
    if (total_bytes > MAX_ALLOC_SIZE) {
        return NULL;
    }

    return malloc(total_bytes);
}

// Alternative: calloc verwenden, das eingebautes Overflow-Checking hat
void* fixed_alloc_array_v2(size_t count, size_t element_size) {
    if (count > MAX_ALLOC_SIZE / element_size) {
        return NULL;
    }

    // calloc prüft intern auf Overflow
    return calloc(count, element_size);
}
// Gefixt: Kapazität in Java validieren
import java.util.HashMap;

public class FixedHashMap {
    private static final int MAX_CAPACITY = 1000000;  // 1 Million Einträge max

    public HashMap<String, String> createFromInput(int capacity)
            throws IllegalArgumentException {
        // Gefixt: Kapazität validieren
        if (capacity < 0) {
            throw new IllegalArgumentException("Kapazität darf nicht negativ sein");
        }
        if (capacity > MAX_CAPACITY) {
            throw new IllegalArgumentException("Kapazität überschreitet Maximum: " + MAX_CAPACITY);
        }

        return new HashMap<>(capacity);
    }
}
// Gefixt: Netzwerkdaten vor Allokation validieren
#include <arpa/inet.h>

#define MAX_DATA_LENGTH (1024 * 1024)  // 1 MB max
#define MAX_ITEM_COUNT 10000

typedef struct {
    uint32_t data_length;
    uint32_t item_count;
} PacketHeader;

int fixed_process_packet(PacketHeader* header) {
    // Gefixt: Von Network-Byte-Order konvertieren
    uint32_t data_length = ntohl(header->data_length);
    uint32_t item_count = ntohl(header->item_count);

    // Gefixt: Vor Allokation validieren
    if (data_length > MAX_DATA_LENGTH) {
        return -1;  // Übergroße Daten ablehnen
    }

    if (item_count > MAX_ITEM_COUNT) {
        return -1;  // Zu viele Elemente ablehnen
    }

    // Gefixt: Auf Overflow in Multiplikation prüfen
    if (item_count > SIZE_MAX / sizeof(Item)) {
        return -1;
    }

    char* data = malloc(data_length);
    if (data == NULL && data_length > 0) {
        return -1;
    }

    Item* items = malloc(item_count * sizeof(Item));
    if (items == NULL && item_count > 0) {
        free(data);
        return -1;
    }

    // Daten verarbeiten...

    free(items);
    free(data);
    return 0;
}

CVE-Beispiele

  • CVE-2019-19911: Speicherallokation mit übermäßiger Größe bei Bildverarbeitung.
  • CVE-2010-3701: Übermäßige Speicherallokation in Virtualisierungssoftware.
  • CVE-2008-1708: Integer Overflow führt zu unterdimensionierter Allokation.
  • CVE-2008-0977: Ungeprüfter Allokationsgrößenwert aus Netzwerkdaten.

Referenzen

  1. MITRE Corporation. "CWE-789: Memory Allocation with Excessive Size Value." https://cwe.mitre.org/data/definitions/789.html
  2. CERT C Coding Standard. "MEM35-C. Allocate sufficient memory for an object."
  3. CERT C Coding Standard. "INT30-C. Ensure that unsigned integer operations do not wrap."