Unzureichende Kapselung maschinenabhängiger Funktionalität

Beschreibung

Unzureichende Kapselung maschinenabhängiger Funktionalität tritt auf, wenn ein Produkt oder Code maschinenabhängige Funktionalität verwendet, ohne diese ausreichend vom Rest der Codebasis zu kapseln oder zu isolieren. Dies umfasst die direkte Verwendung hardwarespezifischer Funktionen, Speicherlayout-Annahmen, architekturspezifischer Anweisungen oder plattformspezifischer Systemaufrufe ohne ordnungsgemäße Abstraktionsschichten. Wenn maschinenabhängiger Code in der gesamten Anwendung verstreut ist, wird es schwierig, den Code zu portieren, zu warten und konsistentes Sicherheitsverhalten sicherzustellen.

Risiko

Unzureichend gekapselte maschinenabhängige Funktionalität hat Sicherheitsimplikationen. Code, der bestimmte Speicherlayouts annimmt, kann auf verschiedenen Architekturen Buffer Overflows haben. Zeigerarithmetik basierend auf Speicherlayout-Annahmen kann Speicherkorruption verursachen. Architekturspezifische Optimierungen können auf anderen Plattformen Schwachstellen einführen. Sicherheitsrelevante Operationen können sich auf verschiedenen Maschinen unterschiedlich verhalten. Code-Review für Sicherheit wird durch verstreuten maschinenabhängigen Code erschwert. Tests können nicht alle architekturspezifischen Variationen abdecken.

Lösung

Kapseln Sie alle maschinenabhängige Funktionalität in dedizierten Modulen oder Abstraktionsschichten. Verwenden Sie Compiler-bereitgestellte Abstraktionen für architekturspezifische Funktionen. Vermeiden Sie Annahmen über Speicherlayout, Datenausrichtung oder Struktur-Padding. Verwenden Sie Standardbibliotheksfunktionen anstelle maschinenspezifischer Implementierungen. Dokumentieren Sie alle notwendigen Maschinenabhängigkeiten klar. Testen Sie auf mehreren Architekturen wenn möglich. Verwenden Sie statische Analysetools, um maschinenabhängige Codemuster zu erkennen. Wenden Sie das Adapter-Pattern an, um Plattformunterschiede zu isolieren.

Häufige Auswirkungen

AuswirkungDetails
AndereBereich: Ändere

Reduzierte Wartbarkeit - Maschinenabhängiger Code verstreut ist schwer zu warten.
IntegritätBereich: Integrität

Speicherkorruption - Speicherlayout-Annahmen gelten möglicherweise nicht auf allen Architekturen.
AndereBereich: Ändere

Reduzierte Portabilität - Code kann auf verschiedenen Maschinen nicht korrekt ausgeführt werden.

Beispielcode

Anfälliger Code

// Anfällig: Direkte Abhängigkeit von Speicherlayout
#include <stdio.h>

void vulnerable_memory_assumption() {
    char a;
    char b;

    // Anfällig: Nimmt an, b ist benachbart zu a im Speicher
    // Dies ist NICHT garantiert über verschiedene Compiler/Architekturen
    *(&a + 1) = 'X';  // Kann zufälligen Speicher korrumpieren!

    // Auf manchen Architekturen:
    // - Variablen können umgeordnet werden
    // - Verschiedenes Ausrichtungs-Padding kann existieren
    // - Stack kann in verschiedene Richtungen wachsen
}

// Anfällig: Struktur mit angenommenem Layout
struct VulnerablePacket {
    char type;
    // Padding wird hier auf manchen Architekturen angenommen, auf anderen nicht
    int length;
    char data[100];
};

void vulnerable_struct_access(char* buffer) {
    // Anfällig: Nimmt spezifisches Speicherlayout der Struktur an
    struct VulnerablePacket* packet = (struct VulnerablePacket*)buffer;

    // Nimmt kein Padding zwischen type und length an
    // Kann falsche Daten auf Architekturen mit anderer Ausrichtung lesen
    printf("Length: %d\n", packet->length);
}

// Anfällig: Endianness-Annahme mit Geschäftslogik vermischt
uint32_t vulnerable_parse_network_int(unsigned char* buffer) {
    // Anfällig: Nimmt Little-Endian überall an
    // Direkter Zugriff auf Buffer als int* ohne Kapselung
    return *(uint32_t*)buffer;
}

// Anfällig: Architekturspezifische Optimierung ohne Kapselung
void vulnerable_fast_copy(void* dest, void* src, size_t size) {
    // Anfällig: x86-spezifischer Code in Anwendung verstreut
#ifdef __x86_64__
    // SSE-Anweisungen direkt verwenden
    __asm__ volatile (
        "rep movsb"
        : "+D"(dest), "+S"(src), "+c"(size)
        :
        : "memory"
    );
#else
    // Fallback ist inkonsistent mit optimiertem Pfad
    memcpy(dest, src, size);
#endif
}
// Anfällig: C++ mit verstreutem maschinenabhängigem Code
class VulnerableSerializer {
public:
    // Anfällig: Zeigergrößen-Annahme
    void serialize_pointer(std::ostream& out, void* ptr) {
        // Nimmt Pointer sind 4 Bytes an
        uint32_t addr = (uint32_t)(uintptr_t)ptr;  // Abgeschnitten auf 64-Bit!
        out.write(reinterpret_cast<char*>(&addr), sizeof(addr));
    }

    // Anfällig: Float-Repräsentations-Annahme
    void serialize_float(std::ostream& out, float value) {
        // Nimmt IEEE 754-Repräsentation überall an
        // Nimmt gleiche Endianness wie lesendes System an
        out.write(reinterpret_cast<char*>(&value), sizeof(float));
    }

    // Anfällig: Struktur-Packing-Annahme
    template<typename T>
    void serialize_struct(std::ostream& out, const T& obj) {
        // Nimmt kein Padding in Struktur T an
        // Nimmt gleiche Ausrichtung wie lesendes System an
        out.write(reinterpret_cast<const char*>(&obj), sizeof(T));
    }
};

// Anfällig: Plattformspezifische Intrinsics ohne Kapselung
class VulnerableBitOperations {
public:
    int count_leading_zeros(uint32_t value) {
        // Anfällig: Architekturspezifischer Code in Anwendungsschicht
#if defined(__GNUC__)
        return __builtin_clz(value);
#elif defined(_MSC_VER)
        unsigned long index;
        _BitScanReverse(&index, value);
        return 31 - index;
#else
        // Fallback hat möglicherweise anderes Grenzfall-Verhalten
        int count = 0;
        while (!(value & 0x80000000) && count < 32) {
            value <<= 1;
            count++;
        }
        return count;
#endif
    }
};

Korrigierter Code

// Korrigiert: Ordnungsgemäß gekapselte maschinenabhängige Funktionalität
#include <stdio.h>
#include <stdint.h>
#include <string.h>

// Gekapselte Plattformerkennung
typedef struct {
    int is_little_endian;
    int pointer_size;
    int int_size;
} PlatformInfo;

static PlatformInfo get_platform_info() {
    PlatformInfo info;

    // Endianness erkennen
    uint16_t test = 1;
    info.is_little_endian = (*(uint8_t*)&test == 1);

    // Größen aufzeichnen
    info.pointer_size = sizeof(void*);
    info.int_size = sizeof(int);

    return info;
}

// Korrigiert: Speicherzugriff durch ordnungsgemäße Interfaces
typedef struct {
    uint8_t type;
    uint8_t padding[3];  // Explizites Padding
    uint32_t length;
    uint8_t data[100];
} FixedPacket;

// Korrigiert: Expliziten Bytezugriff verwenden, nicht Annahmen
FixedPacket fixed_parse_packet(const uint8_t* buffer) {
    FixedPacket packet;

    packet.type = buffer[0];

    // Korrigiert: Explizite Byte-Reihenfolge-Behandlung, nicht Speicherlayout-Annahme
    packet.length = ((uint32_t)buffer[4] << 24) |
                    ((uint32_t)buffer[5] << 16) |
                    ((uint32_t)buffer[6] << 8) |
                    ((uint32_t)buffer[7]);

    memcpy(packet.data, buffer + 8, sizeof(packet.data));

    return packet;
}

// Korrigiert: Gekapselte Endianness-Behandlung
// In dediziertem Header: byte_order.h
uint32_t read_uint32_be(const uint8_t* buffer) {
    return ((uint32_t)buffer[0] << 24) |
           ((uint32_t)buffer[1] << 16) |
           ((uint32_t)buffer[2] << 8) |
           ((uint32_t)buffer[3]);
}

uint32_t read_uint32_le(const uint8_t* buffer) {
    return ((uint32_t)buffer[3] << 24) |
           ((uint32_t)buffer[2] << 16) |
           ((uint32_t)buffer[1] << 8) |
           ((uint32_t)buffer[0]);
}

void write_uint32_be(uint8_t* buffer, uint32_t value) {
    buffer[0] = (value >> 24) & 0xFF;
    buffer[1] = (value >> 16) & 0xFF;
    buffer[2] = (value >> 8) & 0xFF;
    buffer[3] = value & 0xFF;
}

// Korrigiert: Gekapselte Speicherkopie - plattformspezifisch an einer Stelle
// In dedizierter Datei: platform_memory.c
void platform_fast_memcpy(void* dest, const void* src, size_t size) {
#if defined(__x86_64__) && defined(USE_SIMD_OPTIMIZATIONS)
    // Optimierte x86-64-Implementierung
    if (size >= 128) {
        x86_simd_memcpy(dest, src, size);
        return;
    }
#elif defined(__aarch64__) && defined(USE_SIMD_OPTIMIZATIONS)
    // Optimierte ARM64-Implementierung
    if (size >= 128) {
        arm_neon_memcpy(dest, src, size);
        return;
    }
#endif
    // Portabler Fallback
    memcpy(dest, src, size);
}
// Korrigiert: C++ mit ordnungsgemäß gekapseltem maschinenabhängigem Code

// Byte-Reihenfolge-Utilities - in dediziertem Header
namespace ByteOrder {

inline uint32_t read_be32(const uint8_t* buffer) {
    return (static_cast<uint32_t>(buffer[0]) << 24) |
           (static_cast<uint32_t>(buffer[1]) << 16) |
           (static_cast<uint32_t>(buffer[2]) << 8) |
           static_cast<uint32_t>(buffer[3]);
}

inline void write_be32(uint8_t* buffer, uint32_t value) {
    buffer[0] = static_cast<uint8_t>(value >> 24);
    buffer[1] = static_cast<uint8_t>(value >> 16);
    buffer[2] = static_cast<uint8_t>(value >> 8);
    buffer[3] = static_cast<uint8_t>(value);
}

inline uint64_t read_be64(const uint8_t* buffer) {
    return (static_cast<uint64_t>(read_be32(buffer)) << 32) |
           read_be32(buffer + 4);
}

}  // namespace ByteOrder

// Korrigiert: Portabler Serializer
class FixedSerializer {
public:
    // Korrigiert: Festbreiten-Integer mit expliziter Byte-Reihenfolge verwenden
    void serialize_uint64(std::ostream& out, uint64_t value) {
        uint8_t buffer[8];
        ByteOrder::write_be64(buffer, value);
        out.write(reinterpret_cast<char*>(buffer), 8);
    }

    // Korrigiert: Pointer als Adresse serialisieren (mit expliziter Größe)
    void serialize_address(std::ostream& out, uintptr_t addr) {
        // Immer als 64-Bit für Kompatibilität serialisieren
        serialize_uint64(out, static_cast<uint64_t>(addr));
    }

    // Korrigiert: Float mit expliziter Repräsentation
    void serialize_float(std::ostream& out, float value) {
        static_assert(std::numeric_limits<float>::is_iec559,
                     "Erfordert IEEE 754 Floats");
        uint32_t bits;
        std::memcpy(&bits, &value, sizeof(bits));
        uint8_t buffer[4];
        ByteOrder::write_be32(buffer, bits);
        out.write(reinterpret_cast<char*>(buffer), 4);
    }

    // Korrigiert: Struktur-Serialisierung durch expliziten Feldzugriff
    template<typename T>
    void serialize_fields(std::ostream& out, const T& obj) {
        // Jeder Typ muss seine eigene serialize-Methode bereitstellen
        obj.serialize(out, *this);
    }
};

// Korrigiert: Gekapselte Bit-Operationen
// In dediziertem Header: bit_ops.h
class BitOperations {
public:
    static int count_leading_zeros(uint32_t value) {
        if (value == 0) return 32;

#if defined(__GNUC__) || defined(__clang__)
        return __builtin_clz(value);
#elif defined(_MSC_VER)
        unsigned long index;
        _BitScanReverse(&index, value);
        return 31 - static_cast<int>(index);
#else
        return portable_clz(value);
#endif
    }

private:
    static int portable_clz(uint32_t value) {
        // de Bruijn-Sequenz-Methode - portabel und konsistent
        static const int debruijn32[32] = {
            0, 31, 9, 30, 3, 8, 18, 29, 2, 5, 7, 14, 12, 17,
            22, 28, 1, 10, 4, 19, 6, 15, 13, 23, 11, 20, 16,
            24, 21, 25, 26, 27
        };

        value |= value >> 1;
        value |= value >> 2;
        value |= value >> 4;
        value |= value >> 8;
        value |= value >> 16;
        value++;

        return debruijn32[value * 0x076be629 >> 27];
    }
};

// Korrigiert: Plattformabstraktions-Interface
class IPlatformMemory {
public:
    virtual ~IPlatformMemory() = default;
    virtual void fast_copy(void* dest, const void* src, size_t size) = 0;
    virtual void secure_zero(void* ptr, size_t size) = 0;
};

class PortablePlatformMemory : public IPlatformMemory {
public:
    void fast_copy(void* dest, const void* src, size_t size) override {
        std::memcpy(dest, src, size);
    }

    void secure_zero(void* ptr, size_t size) override {
        volatile unsigned char* p = static_cast<volatile unsigned char*>(ptr);
        while (size--) {
            *p++ = 0;
        }
    }
};

#ifdef __x86_64__
class X86PlatformMemory : public IPlatformMemory {
public:
    void fast_copy(void* dest, const void* src, size_t size) override {
        if (size >= 256 && supports_avx()) {
            avx_memcpy(dest, src, size);
        } else {
            std::memcpy(dest, src, size);
        }
    }

    void secure_zero(void* ptr, size_t size) override {
        // x86-spezifisches sicheres Nullen mit Speicherbarriere
        std::memset(ptr, 0, size);
        std::atomic_thread_fence(std::memory_order_seq_cst);
    }

private:
    bool supports_avx() { /* CPUID prüfen */ return false; }
    void avx_memcpy(void* dest, const void* src, size_t size) { }
};
#endif

// Factory zur Erstellung geeigneter Implementierung
std::unique_ptr<IPlatformMemory> create_platform_memory() {
#ifdef __x86_64__
    return std::make_unique<X86PlatformMemory>();
#else
    return std::make_unique<PortablePlatformMemory>();
#endif
}

CVE-Beispiele

Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Codequalitätsproblem und keine direkte Sicherheitsschwachstelle darstellt.


Verwandte CWEs

  • CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior (Eltern)
  • CWE-1061: Insufficient Encapsulation (Eltern)
  • CWE-188: Reliance on Data/Memory Layout (Kind)
  • CWE-1102: Reliance on Machine-Dependent Data Representation (Peer)

Referenzen

  1. MITRE Corporation. "CWE-1105: Insufficient Encapsulation of Machine-Dependent Functionality." https://cwe.mitre.org/data/definitions/1105.html
  2. CERT C Coding Standard. "DCL39-C: Avoid information leakage when passing a structure across a trust boundary."