Assignment of a Fixed Address to a Pointer

Description

Assignment of a Fixed Address to a Pointer occurs when code assigns a hardcoded memory address to a pointer variable and then uses that pointer to access memory. Memory addresses are meaningful only in specific contexts—they change between runs, systems, and configurations. Code that relies on fixed addresses typically works only by coincidence and breaks unpredictably. This pattern is especially dangerous in modern systems with Address Space Layout Randomization (ASLR) and memory protection.

Risk

Fixed address assignments cause crashes when the expected memory isn't mapped or accessible. On systems without ASLR, attackers can exploit predictable addresses for attacks. The code is non-portable—addresses valid on one system fail on others. In embedded systems, fixed addresses may reference hardware registers, but this requires careful consideration of memory mapping. Using address 0 explicitly creates NULL pointer dereferences. Security mitigations like ASLR are bypassed if code assumes fixed addresses.

Solution

Never hardcode memory addresses except in platform-specific embedded code with documented hardware memory maps. Use proper memory allocation (malloc, new) instead of fixed addresses. For hardware access in embedded systems, use documented memory-mapped I/O with proper abstractions. If fixed addresses are unavoidable, encapsulate them with clear documentation and platform checks. Use symbolic constants defined by the platform for special addresses. Enable ASLR and don't write code that depends on address predictability.

Common Consequences

ImpactDetails
AvailabilityScope: Crash

Accessing unmapped or protected memory causes segmentation faults.
SecurityScope: Bypassed Mitigations

Fixed addresses bypass ASLR and other security features.
PortabilityScope: Non-Portable Code

Code fails on different systems, configurations, or runs.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Hardcoded memory address
void access_memory_vulnerable() {
    int* ptr = (int*)0x12345678;  // Fixed address!
    *ptr = 42;  // Crash if address not mapped!
}

// VULNERABLE: Explicit NULL assignment then dereference
void null_access_vulnerable() {
    int* ptr = (int*)0x0;  // NULL pointer!
    *ptr = 42;  // Guaranteed crash!
}

// VULNERABLE: Assuming stack addresses
void stack_assumption_vulnerable() {
    // Assuming stack starts at specific location
    char* stack_base = (char*)0x7FFF0000;
    // This won't work with ASLR
    read_stack(stack_base);
}

// VULNERABLE: Hardcoded buffer address
void buffer_vulnerable() {
    char* buffer = (char*)0x08048000;  // Assuming fixed code segment
    strcpy(buffer, "data");  // Likely crash or security issue
}

// VULNERABLE: Assumes predictable heap location
void heap_assumption_vulnerable() {
    // Wrong! Heap addresses are randomized with ASLR
    int* heap_data = (int*)0x602000;
    *heap_data = 100;
}

// VULNERABLE: Type punning via fixed address
void type_pun_vulnerable() {
    // Trying to access specific memory as different type
    float* f = (float*)0xDEADBEEF;
    printf("%f\n", *f);  // Crash!
}

// VULNERABLE: Embedded code without proper abstraction
void embedded_dangerous() {
    // Magic number for hardware register
    volatile int* reg = (volatile int*)0x40001000;
    *reg = 0x01;  // May work on specific hardware, fails elsewhere
}

// VULNERABLE: Array at fixed address
void array_vulnerable() {
    int (*array)[100] = (int(*)[100])0xB0000000;
    (*array)[0] = 1;  // Address may not be valid
}
// VULNERABLE: C++ with fixed addresses
class VulnerableHardware {
public:
    void writeRegister(int value) {
        // Hardcoded hardware address
        volatile int* reg = reinterpret_cast<volatile int*>(0x40001234);
        *reg = value;
    }

    int readMemory() {
        // Arbitrary memory read
        int* ptr = reinterpret_cast<int*>(0x08000000);
        return *ptr;  // Crash on most systems!
    }
};

// VULNERABLE: Placement new at fixed address
void placementNew_vulnerable() {
    void* addr = reinterpret_cast<void*>(0xC0000000);
    // Assuming this address is valid writable memory
    new (addr) MyClass();  // Very likely to crash
}

// VULNERABLE: Fixed address in template
template<uintptr_t Address>
class FixedMemory_vulnerable {
public:
    void write(int value) {
        *reinterpret_cast<int*>(Address) = value;
    }
};

// Usage
FixedMemory_vulnerable<0x12345678> mem;  // Hardcoded address in type
// VULNERABLE: Security exploit pattern
void exploit_pattern_vulnerable() {
    // Attacker-friendly code that uses predictable addresses
    void (*func_ptr)() = (void (*)())0x08048000;  // Fixed code address
    func_ptr();  // Jump to "predictable" location

    // This pattern is used in exploits against non-ASLR systems
}

// VULNERABLE: Assuming physical memory layout
void physical_memory_vulnerable() {
    // Trying to access video memory directly
    char* video = (char*)0xB8000;  // Legacy VGA text mode address
    video[0] = 'A';  // May crash without proper setup
}

Fixed Code

// SAFE: Use proper memory allocation
void access_memory_safe() {
    int* ptr = malloc(sizeof(int));
    if (ptr != NULL) {
        *ptr = 42;
        free(ptr);
    }
}

// SAFE: Proper NULL handling
void null_handling_safe(int* ptr) {
    if (ptr == NULL) {
        handle_null_error();
        return;
    }
    *ptr = 42;
}

// SAFE: Use standard stack allocation
void stack_safe() {
    char buffer[1024];  // Stack allocated, address determined at runtime
    process(buffer, sizeof(buffer));
}

// SAFE: Dynamic buffer allocation
void buffer_safe() {
    char* buffer = malloc(256);
    if (buffer) {
        strcpy(buffer, "data");
        process(buffer);
        free(buffer);
    }
}

// SAFE: Proper heap allocation
void heap_safe() {
    int* heap_data = malloc(sizeof(int));
    if (heap_data) {
        *heap_data = 100;
        free(heap_data);
    }
}

// SAFE: Embedded code with proper abstraction
// hardware.h
#ifndef HARDWARE_H
#define HARDWARE_H

#ifdef PLATFORM_STM32
    #define GPIO_BASE_ADDR 0x40010800
    #define USART_BASE_ADDR 0x40013800
#elif defined(PLATFORM_AVR)
    #define GPIO_BASE_ADDR 0x20
    #define USART_BASE_ADDR 0xC0
#else
    #error "Platform not supported"
#endif

typedef struct {
    volatile uint32_t CR1;
    volatile uint32_t CR2;
    volatile uint32_t SR;
    volatile uint32_t DR;
} USART_TypeDef;

// Platform-specific accessor
static inline USART_TypeDef* get_usart1(void) {
    return (USART_TypeDef*)USART_BASE_ADDR;
}

#endif

// Usage in embedded code:
void embedded_safe() {
#ifdef PLATFORM_STM32
    USART_TypeDef* usart = get_usart1();
    usart->DR = 'A';
#else
    // Use platform-appropriate code
#endif
}

// SAFE: Memory-mapped I/O with proper checks
int safe_mmio_read(uintptr_t phys_addr, size_t length) {
#ifdef __linux__
    int fd = open("/dev/mem", O_RDONLY);
    if (fd < 0) {
        return -1;
    }

    void* mapped = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, phys_addr);
    if (mapped == MAP_FAILED) {
        close(fd);
        return -1;
    }

    // Use mapped memory safely
    int value = *(volatile int*)mapped;

    munmap(mapped, length);
    close(fd);
    return value;
#else
    return -1;  // Not supported on this platform
#endif
}
// SAFE: C++ with proper memory handling
class SafeHardware {
private:
    volatile uint32_t* registerBase;
    bool initialized;

public:
    SafeHardware() : registerBase(nullptr), initialized(false) {}

    bool initialize(uintptr_t baseAddress) {
#ifdef EMBEDDED_PLATFORM
        // On embedded, verify address is in valid hardware range
        if (isValidHardwareAddress(baseAddress)) {
            registerBase = reinterpret_cast<volatile uint32_t*>(baseAddress);
            initialized = true;
            return true;
        }
#endif
        return false;
    }

    bool writeRegister(size_t offset, uint32_t value) {
        if (!initialized || registerBase == nullptr) {
            return false;
        }
        registerBase[offset] = value;
        return true;
    }
};

// SAFE: Use smart pointers
void safe_allocation() {
    auto ptr = std::make_unique<int>(42);
    // Memory properly managed
}

// SAFE: Memory pool with verified addresses
class SafeMemoryPool {
    std::vector<uint8_t> pool;
    uint8_t* base;

public:
    SafeMemoryPool(size_t size) : pool(size) {
        base = pool.data();  // Address determined at runtime
    }

    template<typename T>
    T* allocate(size_t offset) {
        if (offset + sizeof(T) > pool.size()) {
            return nullptr;  // Bounds check
        }
        return reinterpret_cast<T*>(base + offset);
    }
};

// SAFE: RAII for memory mapped regions
class MemoryMappedRegion {
    void* addr;
    size_t length;

public:
    MemoryMappedRegion(size_t len) : addr(nullptr), length(len) {
        addr = mmap(nullptr, length, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (addr == MAP_FAILED) {
            addr = nullptr;
        }
    }

    ~MemoryMappedRegion() {
        if (addr) {
            munmap(addr, length);
        }
    }

    void* get() { return addr; }
    bool valid() { return addr != nullptr; }
};

// Usage
void use_mapped_safe() {
    MemoryMappedRegion region(4096);
    if (region.valid()) {
        // Use region.get() safely
    }
}

Exploited in the Wild

Return-to-libc Without ASLR

Before ASLR, attackers used fixed addresses in libc for return-oriented programming attacks.

Embedded Device Exploits

Embedded devices with fixed memory layouts have been exploited using known addresses.

Kernel Exploits

Kernel vulnerabilities have been exploited using known addresses before kernel ASLR (KASLR) was widespread.


Tools to test/exploit

  • Valgrind — detects invalid memory access.

  • Static analyzers — flag hardcoded addresses.

  • ASLR verification tools — check if ASLR is effective.

  • Memory debuggers — detect access to unmapped memory.


CVE Examples

  • Exploits against non-ASLR systems using predictable addresses.

  • Kernel exploits using fixed kernel addresses.

  • Embedded system exploits leveraging fixed memory maps.


References

  1. MITRE. "CWE-587: Assignment of a Fixed Address to a Pointer." https://cwe.mitre.org/data/definitions/587.html

  2. CERT C. "INT36-C: Converting a pointer to integer or integer to pointer." https://wiki.sei.cmu.edu/confluence/display/c/