CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations

Description

CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations occurs when the CPU lacks hardware support or has disabled features that prevent simultaneous write and execute access to memory regions. Modern CPUs typically include a special bit (often called NX/XD bit) that segregates memory into code (executable) and data (non-executable) regions. Some processors implement this through Memory Protection Units (MPU) or Memory Management Units (MMU). However, CPUs without these features—or with disabled protection—cannot enforce write/execute exclusivity, allowing attackers to inject and run malicious code.

Risk

Missing write/execute exclusivity has severe security implications. Attackers can inject malicious code into writable memory. Injected code can be executed without restrictions. Buffer overflow exploits become trivial. Code injection attacks succeed without additional bypasses. Return-oriented programming may not be necessary. Shellcode injection is straightforward. Memory corruption leads directly to code execution. Standard security mitigations are ineffective.

Solution

Implement a dedicated bit for marking data as non-executable, or incorporate MMU/MPU support if unavailable. Deploy SoC interconnect firewalls to emulate write-exclusivity when hardware support is absent. Enable DEP/NX bit in operating system configuration. Use Memory Protection Units on microcontrollers. Implement software-based W^X enforcement where hardware support is limited. Consider hardware upgrades for security-critical applications.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Execute Unauthorized Code - Attackers can inject and execute arbitrary code, potentially accessing sensitive data.
IntegrityScope: Integrity

Execute Unauthorized Code - Malicious code execution allows modification of system data and behavior.

Example Code

Vulnerable Code

// Vulnerable: System without W^X enforcement

#include <stdint.h>
#include <string.h>

// Example: Microcontroller without MPU
// All memory is both writable and executable

char data_buffer[256];  // This buffer is executable!

void vulnerable_input_handler(const char* input, size_t len) {
    // VULNERABLE: Copies user input to executable memory
    if (len < sizeof(data_buffer)) {
        memcpy(data_buffer, input, len);
    }
    // Attacker can inject shellcode into data_buffer
    // and jump to it for execution
}

// VULNERABLE: Function pointer in writable memory
typedef void (*callback_t)(void);
callback_t user_callback = NULL;

void vulnerable_set_callback(callback_t cb) {
    // VULNERABLE: Attacker can overwrite this with shellcode address
    user_callback = cb;
}

void vulnerable_execute_callback(void) {
    if (user_callback) {
        // VULNERABLE: Executes whatever address is stored
        user_callback();
    }
}

// Attack scenario
void exploit_no_wxe(void) {
    // Shellcode to execute
    unsigned char shellcode[] = {
        0x31, 0xc0,             // xor eax, eax
        0x50,                   // push eax
        0x68, 0x2f, 0x2f, 0x73, 0x68, // push "//sh"
        0x68, 0x2f, 0x62, 0x69, 0x6e, // push "/bin"
        0x89, 0xe3,             // mov ebx, esp
        0x50,                   // push eax
        0x53,                   // push ebx
        0x89, 0xe1,             // mov ecx, esp
        0xb0, 0x0b,             // mov al, 0x0b
        0xcd, 0x80              // int 0x80
    };

    // Copy shellcode to writable buffer
    memcpy(data_buffer, shellcode, sizeof(shellcode));

    // Jump to shellcode - works because buffer is executable
    ((void(*)())data_buffer)();
}
// Vulnerable: Memory controller without execute protection

module vulnerable_memory_controller (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire fetch_enable,  // Instruction fetch
    output reg [31:0] read_data,
    output reg operation_complete
);

    // Memory array
    reg [31:0] memory [0:16383];

    // VULNERABLE: No separation between code and data
    // All memory regions can be both written and executed

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            operation_complete <= 1'b0;
        end
        else begin
            if (write_enable) begin
                // VULNERABLE: Write allowed to any address
                // Including code regions
                memory[addr[15:2]] <= write_data;
                operation_complete <= 1'b1;
            end
            else if (read_enable || fetch_enable) begin
                // VULNERABLE: Execute (fetch) allowed from any address
                // Including data regions
                read_data <= memory[addr[15:2]];
                operation_complete <= 1'b1;
            end
            else begin
                operation_complete <= 1'b0;
            end
        end
    end

    // No access control based on:
    // - Memory region attributes
    // - Execute permission bits
    // - Write permission to code regions

endmodule

// Vulnerable: MPU not configured
module vulnerable_mpu_unused (
    input wire clk,
    input wire reset_n,
    // MPU configuration ports exist but unused
    input wire mpu_enable,
    input wire [31:0] mpu_region_base,
    input wire [31:0] mpu_region_size,
    input wire [7:0] mpu_region_attr,
    // Memory access
    input wire [31:0] access_addr,
    input wire access_write,
    input wire access_execute,
    output reg access_allowed
);

    // VULNERABLE: MPU exists but is not enabled
    // All accesses are allowed regardless of attributes

    always @(*) begin
        // Should check MPU regions and permissions
        // But MPU is disabled, so always allow
        access_allowed = 1'b1;
    end

endmodule

Fixed Code

// Fixed: System with W^X enforcement

#include <stdint.h>
#include <string.h>
#include <sys/mman.h>

// Fixed: Data buffer marked as non-executable
__attribute__((section(".data.noexec")))
char data_buffer[256];

void secure_init_memory_protection(void) {
    // FIXED: Mark data sections as non-executable
    extern char __data_start, __data_end;
    size_t data_size = &__data_end - &__data_start;

    // Set data segment as read-write but NOT executable
    if (mprotect(&__data_start, data_size, PROT_READ | PROT_WRITE) < 0) {
        // Handle error - fail secure
        abort();
    }

    // FIXED: Mark code sections as non-writable
    extern char __text_start, __text_end;
    size_t text_size = &__text_end - &__text_start;

    if (mprotect(&__text_start, text_size, PROT_READ | PROT_EXEC) < 0) {
        abort();
    }
}

void secure_input_handler(const char* input, size_t len) {
    // FIXED: Data buffer is not executable
    // Even if attacker injects code, it cannot execute
    if (len < sizeof(data_buffer)) {
        memcpy(data_buffer, input, len);
    }
    // Attempting to execute data_buffer will cause a fault
}

// FIXED: Function pointer with validation
typedef void (*callback_t)(void);
static callback_t registered_callbacks[16] = {0};
static size_t num_callbacks = 0;

int secure_register_callback(callback_t cb) {
    // FIXED: Validate callback is in code section
    extern char __text_start, __text_end;

    if ((char*)cb < &__text_start || (char*)cb >= &__text_end) {
        // Callback not in code section - reject
        return -1;
    }

    if (num_callbacks < 16) {
        registered_callbacks[num_callbacks++] = cb;
        return 0;
    }
    return -1;
}

void secure_execute_callback(size_t index) {
    if (index < num_callbacks && registered_callbacks[index]) {
        // FIXED: Only execute validated callbacks
        registered_callbacks[index]();
    }
}

// Fixed: MPU configuration for Cortex-M
void secure_configure_mpu(void) {
    // Disable MPU during configuration
    MPU->CTRL = 0;

    // Region 0: Flash (code) - Read-only, Executable
    MPU->RNR = 0;
    MPU->RBAR = FLASH_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x11 << MPU_RASR_SIZE_Pos) |    // 256KB
                (0x6 << MPU_RASR_AP_Pos) |       // Read-only
                (0 << MPU_RASR_XN_Pos);          // Executable

    // Region 1: RAM (data) - Read-Write, Non-Executable
    MPU->RNR = 1;
    MPU->RBAR = SRAM_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x10 << MPU_RASR_SIZE_Pos) |    // 128KB
                (0x3 << MPU_RASR_AP_Pos) |       // Read-Write
                (1 << MPU_RASR_XN_Pos);          // FIXED: Non-Executable

    // Region 2: Peripherals - Read-Write, Non-Executable
    MPU->RNR = 2;
    MPU->RBAR = PERIPH_BASE;
    MPU->RASR = MPU_RASR_ENABLE_Msk |
                (0x1C << MPU_RASR_SIZE_Pos) |    // 512MB
                (0x3 << MPU_RASR_AP_Pos) |
                (1 << MPU_RASR_XN_Pos);          // Non-Executable

    // Enable MPU with default memory map for privileged access
    MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;

    // Memory barrier
    __DSB();
    __ISB();
}
// Fixed: Memory controller with W^X enforcement

module secure_memory_controller (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire read_enable,
    input wire fetch_enable,
    output reg [31:0] read_data,
    output reg operation_complete,
    output reg access_violation
);

    // Memory array
    reg [31:0] memory [0:16383];

    // FIXED: Memory region attributes
    // Bit 0: Writable
    // Bit 1: Executable
    // W^X: These bits should never both be set
    reg [1:0] region_attr [0:15];  // 16 regions of 4KB each

    // Region calculation
    wire [3:0] region_index = addr[15:12];
    wire region_writable = region_attr[region_index][0];
    wire region_executable = region_attr[region_index][1];

    // FIXED: Initialize regions with W^X enforcement
    initial begin
        // Code region (0-3): Executable, NOT writable
        region_attr[0] = 2'b10;
        region_attr[1] = 2'b10;
        region_attr[2] = 2'b10;
        region_attr[3] = 2'b10;

        // Data region (4-11): Writable, NOT executable
        region_attr[4] = 2'b01;
        region_attr[5] = 2'b01;
        region_attr[6] = 2'b01;
        region_attr[7] = 2'b01;
        region_attr[8] = 2'b01;
        region_attr[9] = 2'b01;
        region_attr[10] = 2'b01;
        region_attr[11] = 2'b01;

        // Stack region (12-13): Writable, NOT executable
        region_attr[12] = 2'b01;
        region_attr[13] = 2'b01;

        // Reserved (14-15): Neither writable nor executable
        region_attr[14] = 2'b00;
        region_attr[15] = 2'b00;
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            operation_complete <= 1'b0;
            access_violation <= 1'b0;
        end
        else begin
            access_violation <= 1'b0;

            if (write_enable) begin
                // FIXED: Check write permission
                if (region_writable) begin
                    memory[addr[15:2]] <= write_data;
                    operation_complete <= 1'b1;
                end
                else begin
                    // FIXED: Deny write to non-writable region
                    access_violation <= 1'b1;
                    operation_complete <= 1'b0;
                end
            end
            else if (fetch_enable) begin
                // FIXED: Check execute permission
                if (region_executable) begin
                    read_data <= memory[addr[15:2]];
                    operation_complete <= 1'b1;
                end
                else begin
                    // FIXED: Deny execution from non-executable region
                    access_violation <= 1'b1;
                    operation_complete <= 1'b0;
                end
            end
            else if (read_enable) begin
                // Data read allowed from any readable region
                read_data <= memory[addr[15:2]];
                operation_complete <= 1'b1;
            end
            else begin
                operation_complete <= 1'b0;
            end
        end
    end

endmodule

// Fixed: MPU properly configured and enabled
module secure_mpu_enabled (
    input wire clk,
    input wire reset_n,
    input wire mpu_enable,
    input wire [31:0] access_addr,
    input wire access_write,
    input wire access_execute,
    output reg access_allowed,
    output reg access_fault
);

    // MPU region configuration
    reg [31:0] region_base [0:7];
    reg [31:0] region_limit [0:7];
    reg region_writable [0:7];
    reg region_executable [0:7];
    reg region_enabled [0:7];

    // Find matching region
    reg region_found;
    reg [2:0] matched_region;
    reg match_writable;
    reg match_executable;

    integer i;

    always @(*) begin
        region_found = 1'b0;
        matched_region = 3'h0;
        match_writable = 1'b0;
        match_executable = 1'b0;

        // Search regions (higher priority first)
        for (i = 7; i >= 0; i = i - 1) begin
            if (region_enabled[i] &&
                access_addr >= region_base[i] &&
                access_addr < region_limit[i]) begin
                region_found = 1'b1;
                matched_region = i;
                match_writable = region_writable[i];
                match_executable = region_executable[i];
            end
        end
    end

    always @(*) begin
        access_fault = 1'b0;

        if (!mpu_enable) begin
            // FIXED: Default deny when MPU disabled
            access_allowed = 1'b0;
            access_fault = 1'b1;
        end
        else if (!region_found) begin
            // No matching region - deny
            access_allowed = 1'b0;
            access_fault = 1'b1;
        end
        else begin
            // FIXED: Check permissions
            if (access_write && !match_writable) begin
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            else if (access_execute && !match_executable) begin
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            // FIXED: W^X check
            else if (access_write && access_execute) begin
                // Never allow simultaneous write and execute
                access_allowed = 1'b0;
                access_fault = 1'b1;
            end
            else begin
                access_allowed = 1'b1;
            end
        end
    end

endmodule

CVE Examples

  • MCS51 Microcontroller: Lacks both dedicated exclusivity bit and MMU/MPU support, making all memory both writable and executable.
  • Cortex-M without MPU configuration: MPU exists but remains unconfigured, allowing code injection attacks.
  • Various embedded systems and IoT devices without W^X enforcement.

  • CWE-284: Improper Access Control (parent)
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer (related)
  • CWE-94: Improper Control of Generation of Code ('Code Injection') (related)
  • CAPEC-679: Exploitation of Improperly Configured Memory Protections (attack pattern)

References

  1. MITRE Corporation. "CWE-1252: CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations." https://cwe.mitre.org/data/definitions/1252.html
  2. ARM. "Cortex-M MPU Configuration Guide"
  3. Intel. "Execute Disable Bit Functionality"