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
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Data Corruption - Data may be misinterpreted on different platforms. |
| Availability | Scope: Availability DoS: Crash - Alignment violations or incorrect data sizes can cause crashes. |
| Other | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-1102: Reliance on Machine-Dependent Data Representation." https://cwe.mitre.org/data/definitions/1102.html
- CERT C Coding Standard. INT36-C: Converting a pointer to integer or integer to pointer.
- C++ Core Guidelines. "Portability and machine-specific issues."