Reliance on Machine-Dependent Data Representation

Description

Reliance on Machine-Dependent Data Representation occurs when code uses data representations that rely on low-level constructs that may vary across different processors, physical machines, operating systems, or other physical components. This includes assumptions about data type sizes, byte ordering (endianness), structure padding, alignment requirements, and pointer sizes. When code depends on these implementation-specific behaviors, it becomes non-portable and may behave incorrectly or insecurely on different platforms.

Risk

Depending on machine-specific data representation has direct security implications. Integer overflow behavior may differ across platforms, leading to exploitable conditions. Byte order assumptions can cause data corruption when communicating between systems. Structure padding differences can cause buffer overflows or memory corruption. Pointer size assumptions can cause 32-bit vs 64-bit compatibility issues. Serialized data may be misinterpreted on different architectures. Cryptographic operations may produce different results on different platforms. Memory alignment violations can cause crashes or undefined behavior.

Solution

Use fixed-width integer types (int32_t, uint64_t) instead of platform-dependent types. Explicitly handle byte ordering when serializing/deserializing data. Use standardized serialization formats (JSON, Protocol Buffers, MessagePack). Avoid casting between pointer types and integers. Do not assume specific structure padding or alignment. Use sizeof() and offsetof() for structure field access. Test code on multiple architectures. Use static analysis tools to detect portability issues. Document any intentional platform-specific behavior.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Data Corruption - Data may be misinterpreted on different platforms.
AvailabilityScope: Availability

DoS: Crash - Alignment violations or incorrect data sizes can cause crashes.
OtherScope: Other

Reduce Portability - Code cannot run correctly on different architectures.

Example Code

Vulnerable Code

// Vulnerable: Machine-dependent data representation
#include <stdio.h>
#include <string.h>

// Vulnerable: Assumes specific size for int
struct VulnerablePacket {
    int type;           // Size varies: 2 bytes (16-bit) or 4 bytes (32/64-bit)
    long length;        // Size varies: 4 bytes (32-bit) or 8 bytes (64-bit)
    char data[100];
};

void vulnerable_serialize(struct VulnerablePacket* packet, char* buffer) {
    // Vulnerable: Direct memory copy assumes specific layout
    memcpy(buffer, packet, sizeof(struct VulnerablePacket));
    // Structure padding and field sizes vary by platform!
}

void vulnerable_deserialize(char* buffer, struct VulnerablePacket* packet) {
    // Vulnerable: Assumes buffer has same layout as local structure
    memcpy(packet, buffer, sizeof(struct VulnerablePacket));
}

// Vulnerable: Endianness assumption
uint32_t vulnerable_read_network_int(unsigned char* buffer) {
    // Vulnerable: Assumes little-endian byte order
    return *(uint32_t*)buffer;  // Wrong on big-endian systems!
}

void vulnerable_write_network_int(unsigned char* buffer, uint32_t value) {
    // Vulnerable: Direct memory copy ignores byte order
    *(uint32_t*)buffer = value;  // Data corrupted on different endianness!
}

// Vulnerable: Pointer size assumption
void vulnerable_pointer_handling() {
    void* ptr = malloc(100);

    // Vulnerable: Assumes pointers fit in unsigned int
    unsigned int addr = (unsigned int)ptr;  // Truncates on 64-bit!

    // Later, this truncated address is used...
    void* restored = (void*)(uintptr_t)addr;  // Wrong address!
    free(restored);  // Potential crash or corruption
}

// Vulnerable: Alignment assumption
void vulnerable_alignment(char* buffer) {
    // Vulnerable: Unaligned access may crash on some architectures
    int* int_ptr = (int*)(buffer + 1);  // Misaligned!
    *int_ptr = 42;  // Crash on ARM, SPARC; slow on x86
}
// Vulnerable: C++ with platform-dependent assumptions
#include <cstdint>
#include <fstream>

// Vulnerable: Packed structure with platform assumptions
#pragma pack(push, 1)
struct VulnerableHeader {
    int magic;           // Size varies by platform
    size_t file_size;    // 4 bytes on 32-bit, 8 bytes on 64-bit
    time_t timestamp;    // Varies widely across platforms
    char name[32];
};
#pragma pack(pop)

class VulnerableFileFormat {
public:
    void save(const std::string& filename, const VulnerableHeader& header,
              const std::vector<char>& data) {
        std::ofstream file(filename, std::ios::binary);

        // Vulnerable: Direct structure write
        file.write(reinterpret_cast<const char*>(&header), sizeof(header));
        file.write(data.data(), data.size());
    }

    VulnerableHeader load(const std::string& filename) {
        std::ifstream file(filename, std::ios::binary);
        VulnerableHeader header;

        // Vulnerable: Direct structure read assumes same platform
        file.read(reinterpret_cast<char*>(&header), sizeof(header));

        return header;  // Corrupted if file from different architecture!
    }
};

// Vulnerable: Union type punning
union VulnerableFloatBits {
    float f;
    uint32_t bits;  // Assumes float is 32-bits with specific representation
};

uint32_t vulnerable_float_to_bits(float f) {
    VulnerableFloatBits u;
    u.f = f;
    return u.bits;  // Behavior varies by platform
}
# Vulnerable: Python with machine-dependent assumptions
import struct
import ctypes

class VulnerableSerializer:

    def serialize_int(self, value):
        # Vulnerable: Uses native byte order
        return struct.pack('i', value)  # Endianness varies!

    def deserialize_int(self, data):
        # Vulnerable: Assumes native byte order matches source
        return struct.unpack('i', data)[0]

    def serialize_pointer(self, ptr):
        # Vulnerable: Pointer size varies (4 vs 8 bytes)
        return struct.pack('P', ptr)  # Platform-dependent!


class VulnerableBinaryProtocol:

    def read_message(self, socket):
        # Vulnerable: Assumes 4-byte integer for length
        length_data = socket.recv(4)

        # Vulnerable: Native byte order
        length = struct.unpack('i', length_data)[0]

        return socket.recv(length)

    def write_message(self, socket, data):
        # Vulnerable: Native byte order for length
        length = struct.pack('i', len(data))
        socket.send(length)
        socket.send(data)


# Vulnerable: ctypes with platform assumptions
class VulnerableStruct(ctypes.Structure):
    _fields_ = [
        ('value', ctypes.c_int),    # Size varies
        ('pointer', ctypes.c_void_p), # Size varies
        ('data', ctypes.c_char * 10)
    ]

    def to_bytes(self):
        # Vulnerable: Layout is platform-dependent
        return bytes(self)

    @classmethod
    def from_bytes(cls, data):
        # Vulnerable: Assumes same platform layout
        return cls.from_buffer_copy(data)

Fixed Code

// Fixed: Platform-independent data representation
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>  // For htonl, ntohl

// Fixed: Use fixed-width types
struct FixedPacket {
    uint32_t type;      // Always 4 bytes
    uint64_t length;    // Always 8 bytes
    char data[100];
};

// Fixed: Explicit serialization with defined byte order
size_t fixed_serialize(const struct FixedPacket* packet, uint8_t* buffer) {
    size_t offset = 0;

    // Write type in network byte order (big-endian)
    uint32_t type_be = htonl(packet->type);
    memcpy(buffer + offset, &type_be, sizeof(type_be));
    offset += sizeof(type_be);

    // Write length in network byte order
    uint64_t length_be = htobe64(packet->length);
    memcpy(buffer + offset, &length_be, sizeof(length_be));
    offset += sizeof(length_be);

    // Write data (already bytes)
    memcpy(buffer + offset, packet->data, sizeof(packet->data));
    offset += sizeof(packet->data);

    return offset;
}

int fixed_deserialize(const uint8_t* buffer, struct FixedPacket* packet) {
    size_t offset = 0;

    // Read type in network byte order
    uint32_t type_be;
    memcpy(&type_be, buffer + offset, sizeof(type_be));
    packet->type = ntohl(type_be);
    offset += sizeof(type_be);

    // Read length in network byte order
    uint64_t length_be;
    memcpy(&length_be, buffer + offset, sizeof(length_be));
    packet->length = be64toh(length_be);
    offset += sizeof(length_be);

    // Read data
    memcpy(packet->data, buffer + offset, sizeof(packet->data));

    return 0;
}

// Fixed: Explicit byte order handling
uint32_t fixed_read_network_int(const uint8_t* buffer) {
    // Read bytes explicitly to handle any endianness
    return ((uint32_t)buffer[0] << 24) |
           ((uint32_t)buffer[1] << 16) |
           ((uint32_t)buffer[2] << 8) |
           ((uint32_t)buffer[3]);
}

void fixed_write_network_int(uint8_t* buffer, uint32_t value) {
    // Write bytes explicitly in network order (big-endian)
    buffer[0] = (value >> 24) & 0xFF;
    buffer[1] = (value >> 16) & 0xFF;
    buffer[2] = (value >> 8) & 0xFF;
    buffer[3] = value & 0xFF;
}

// Fixed: Use uintptr_t for pointer/integer conversions
void fixed_pointer_handling() {
    void* ptr = malloc(100);

    // Fixed: Use correct type for pointer-sized integers
    uintptr_t addr = (uintptr_t)ptr;

    // Safe restoration
    void* restored = (void*)addr;
    free(restored);
}

// Fixed: Safe unaligned access
uint32_t fixed_read_unaligned(const uint8_t* buffer) {
    // Read byte-by-byte to handle any alignment
    uint32_t value;
    memcpy(&value, buffer, sizeof(value));
    return value;
}
// Fixed: C++ with platform-independent data handling
#include <cstdint>
#include <fstream>
#include <vector>
#include <cstring>

// Fixed: Explicit field sizes and serialization
struct FixedHeader {
    static constexpr uint32_t MAGIC = 0x46495845;  // "FIXE"
    static constexpr size_t SERIALIZED_SIZE = 4 + 8 + 8 + 32;

    uint32_t magic;
    uint64_t file_size;
    int64_t timestamp;  // Fixed-size timestamp
    char name[32];
};

class FixedFileFormat {
public:
    void save(const std::string& filename, const FixedHeader& header,
              const std::vector<char>& data) {
        std::ofstream file(filename, std::ios::binary);

        // Fixed: Explicit byte-by-byte serialization
        write_uint32_be(file, header.magic);
        write_uint64_be(file, header.file_size);
        write_int64_be(file, header.timestamp);
        file.write(header.name, sizeof(header.name));

        file.write(data.data(), data.size());
    }

    FixedHeader load(const std::string& filename) {
        std::ifstream file(filename, std::ios::binary);
        FixedHeader header{};

        // Fixed: Explicit deserialization
        header.magic = read_uint32_be(file);
        header.file_size = read_uint64_be(file);
        header.timestamp = read_int64_be(file);
        file.read(header.name, sizeof(header.name));

        return header;
    }

private:
    static void write_uint32_be(std::ostream& os, uint32_t value) {
        uint8_t bytes[4] = {
            static_cast<uint8_t>(value >> 24),
            static_cast<uint8_t>(value >> 16),
            static_cast<uint8_t>(value >> 8),
            static_cast<uint8_t>(value)
        };
        os.write(reinterpret_cast<char*>(bytes), 4);
    }

    static void write_uint64_be(std::ostream& os, uint64_t value) {
        uint8_t bytes[8];
        for (int i = 7; i >= 0; --i) {
            bytes[7 - i] = static_cast<uint8_t>(value >> (i * 8));
        }
        os.write(reinterpret_cast<char*>(bytes), 8);
    }

    static void write_int64_be(std::ostream& os, int64_t value) {
        write_uint64_be(os, static_cast<uint64_t>(value));
    }

    static uint32_t read_uint32_be(std::istream& is) {
        uint8_t bytes[4];
        is.read(reinterpret_cast<char*>(bytes), 4);
        return (static_cast<uint32_t>(bytes[0]) << 24) |
               (static_cast<uint32_t>(bytes[1]) << 16) |
               (static_cast<uint32_t>(bytes[2]) << 8) |
               static_cast<uint32_t>(bytes[3]);
    }

    static uint64_t read_uint64_be(std::istream& is) {
        uint8_t bytes[8];
        is.read(reinterpret_cast<char*>(bytes), 8);
        uint64_t result = 0;
        for (int i = 0; i < 8; ++i) {
            result = (result << 8) | bytes[i];
        }
        return result;
    }

    static int64_t read_int64_be(std::istream& is) {
        return static_cast<int64_t>(read_uint64_be(is));
    }
};

// Fixed: Use memcpy for type punning (C++20 also has std::bit_cast)
uint32_t fixed_float_to_bits(float f) {
    static_assert(sizeof(float) == sizeof(uint32_t), "Float must be 32 bits");
    uint32_t bits;
    std::memcpy(&bits, &f, sizeof(bits));
    return bits;
}
# Fixed: Platform-independent Python serialization
import struct
from typing import Tuple


class FixedSerializer:
    """Platform-independent binary serialization."""

    def serialize_int32(self, value: int) -> bytes:
        # Fixed: Explicit big-endian byte order
        return struct.pack('>i', value)  # '>' = big-endian

    def deserialize_int32(self, data: bytes) -> int:
        # Fixed: Explicit big-endian byte order
        return struct.unpack('>i', data)[0]

    def serialize_int64(self, value: int) -> bytes:
        return struct.pack('>q', value)

    def deserialize_int64(self, data: bytes) -> int:
        return struct.unpack('>q', data)[0]

    def serialize_uint32(self, value: int) -> bytes:
        return struct.pack('>I', value)

    def deserialize_uint32(self, data: bytes) -> int:
        return struct.unpack('>I', data)[0]


class FixedBinaryProtocol:
    """Fixed binary protocol with explicit byte order."""

    def __init__(self):
        self._serializer = FixedSerializer()

    def read_message(self, socket) -> bytes:
        # Fixed: Read length with explicit byte order
        length_data = self._recv_exact(socket, 4)
        length = self._serializer.deserialize_uint32(length_data)

        # Validate length to prevent DoS
        if length > 10 * 1024 * 1024:  # 10 MB max
            raise ValueError(f"Message too large: {length}")

        return self._recv_exact(socket, length)

    def write_message(self, socket, data: bytes) -> None:
        # Fixed: Write length with explicit byte order
        length = self._serializer.serialize_uint32(len(data))
        socket.sendall(length)
        socket.sendall(data)

    def _recv_exact(self, socket, size: int) -> bytes:
        """Receive exactly size bytes."""
        data = b''
        while len(data) < size:
            chunk = socket.recv(size - len(data))
            if not chunk:
                raise ConnectionError("Connection closed")
            data += chunk
        return data


# Fixed: Use standardized serialization format
import json

class JsonProtocol:
    """Use JSON for platform-independent data exchange."""

    def serialize(self, data: dict) -> bytes:
        return json.dumps(data).encode('utf-8')

    def deserialize(self, data: bytes) -> dict:
        return json.loads(data.decode('utf-8'))


# For binary efficiency, use Protocol Buffers or MessagePack
try:
    import msgpack

    class MsgPackProtocol:
        """Use MessagePack for efficient platform-independent serialization."""

        def serialize(self, data) -> bytes:
            return msgpack.packb(data)

        def deserialize(self, data: bytes):
            return msgpack.unpackb(data)
except ImportError:
    pass

CVE Examples

Platform-dependent data handling has caused numerous vulnerabilities, particularly in network protocols and file formats where data is exchanged between different systems.


  • CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior (parent)
  • CWE-1105: Insufficient Encapsulation of Machine-Dependent Functionality (peer)
  • CWE-681: Incorrect Conversion between Numeric Types (related)

References

  1. MITRE Corporation. "CWE-1102: Reliance on Machine-Dependent Data Representation." https://cwe.mitre.org/data/definitions/1102.html
  2. CERT C Coding Standard. INT36-C: Converting a pointer to integer or integer to pointer.
  3. C++ Core Guidelines. "Portability and machine-specific issues."