CWE-708: Incorrect Processor Features
Overview
Incorrect processor assumptions occur when code relies on specific CPU features, endianness, word size, or instruction sets without validation, causing failures, vulnerabilities, or exploits when running on different architectures (ARM vs x86, 32-bit vs 64-bit, different endianness).
Risk
Medium: Wrong processor assumptions cause buffer overflows (incorrect pointer sizes), integer overflows (size_t differences), endianness bugs (network protocols), cryptographic weaknesses (missing AES-NI), and crashes on unsupported CPUs.
Remediation Strategy
Use Platform-Independent Types (Primary Defense)
// VULNERABLE - platform-dependent
long user_id; // 32-bit on Windows, 64-bit on Linux x64
int pointer_size = sizeof(void*); // Assumes size
// SECURE - explicit sizes
#include <stdint.h>
int32_t user_id; // Always 32-bit
int64_t timestamp; // Always 64-bit
uintptr_t address; // Pointer-sized integer
size_t buffer_size; // Platform-appropriate size
Handle Endianness Correctly
// VULNERABLE - assumes little-endian
uint32_t value = *(uint32_t*)buffer; // Wrong on big-endian!
// SECURE - portable endian handling
#include <arpa/inet.h>
// Network byte order (big-endian) to host
uint32_t value = ntohl(*(uint32_t*)buffer);
// Host to network byte order
uint32_t network_value = htonl(value);
// Or manual portable conversion
uint32_t value = ((uint32_t)buffer[0] << 24) |
((uint32_t)buffer[1] << 16) |
((uint32_t)buffer[2] << 8) |
(uint32_t)buffer[3];
Check CPU Features at Runtime
#include <cpuid.h>
bool has_aes_ni() {
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
return (ecx & bit_AES) != 0;
}
return false;
}
void encrypt_data(const uint8_t *data, size_t len) {
if (has_aes_ni()) {
// Use hardware-accelerated AES
aes_ni_encrypt(data, len);
} else {
// Fallback to software AES
aes_sw_encrypt(data, len);
}
}
Avoid Architecture-Specific Code
// VULNERABLE - x86-specific inline assembly
void secure_zero(void *ptr, size_t len) {
asm volatile(
"rep stosb"
: "=D"(ptr), "=c"(len)
: "a"(0), "0"(ptr), "1"(len)
: "memory"
); // Won't work on ARM!
}
// SECURE - portable
void secure_zero(void *ptr, size_t len) {
volatile uint8_t *p = ptr;
while (len--) {
*p++ = 0;
}
}
// Or use standard function
#include <string.h>
memset_s(ptr, len, 0, len); // C11
Remediation Steps
Core principle: Avoid ambiguity/undefined behavior in security logic; canonicalize and make rules explicit and deterministic.
- Identify architecture-dependent code
- Locate type mismatches
- Check endianness issues
- Replace with portable types: Use stdint.h types, handle endianness with htonl/ntohl, avoid architecture-specific code
- Add runtime feature detection: Check CPU capabilities before using specialized instructions (AVX, AES-NI, NEON)
- Test on multiple architectures: Verify code works on 32-bit and 64-bit systems, different endianness, ARM and x86
- Review binary formats: Ensure network protocols and file formats explicitly specify byte order
Common Processor Assumptions
Pointer Size:
// Wrong on 64-bit systems
int fd = open(file, O_RDONLY);
void *ptr = (void*)fd; // Truncates on 64-bit!
// Correct
intptr_t fd_as_int = (intptr_t)fd;
void *ptr = (void*)fd_as_int;
Endianness:
// Wrong - binary file format
struct Header {
uint32_t magic; // Written on little-endian
uint32_t version;
};
// Fails on big-endian machines
fwrite(&header, sizeof(header), 1, file);
// Correct - explicit byte order
void write_header(FILE *f, const Header *h) {
uint32_t magic = htonl(h->magic);
uint32_t version = htonl(h->version);
fwrite(&magic, sizeof(magic), 1, f);
fwrite(&version, sizeof(version), 1, f);
}
Alignment:
// May crash on ARM
char buffer[100];
int *ptr = (int*)(buffer + 1); // Unaligned!
*ptr = 42; // SIGBUS on some architectures
// Correct - use memcpy for unaligned access
int value = 42;
memcpy(buffer + 1, &value, sizeof(value));
Word Size:
// Overflow on 32-bit
size_t size = 4UL * 1024 * 1024 * 1024; // 4GB
malloc(size); // Wraps to 0 on 32-bit!
// Check limits
#include <limits.h>
if (size > SIZE_MAX / 4) {
return ERROR;
}
Portable Code Practices
// Use standard types
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
// Platform checks
#if defined(__x86_64__) || defined(_M_X64)
// 64-bit x86
#elif defined(__aarch64__) || defined(_M_ARM64)
// 64-bit ARM
#elif defined(__i386__) || defined(_M_IX86)
// 32-bit x86
#else
#error "Unsupported architecture"
#endif
// Feature detection
#ifdef __AVX2__
// AVX2 available at compile time
#endif
// Runtime detection
if (__builtin_cpu_supports("avx2")) {
// Use AVX2
}
# Python - platform detection
import sys
import struct
if sys.maxsize > 2**32:
# 64-bit Python
pass
else:
# 32-bit Python
pass
# Pack/unpack with explicit byte order
import struct
# Always little-endian
data = struct.pack('<I', value) # < = little-endian
value = struct.unpack('<I', data)[0]
# Always big-endian (network byte order)
data = struct.pack('>I', value) # > = big-endian