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
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Execute Unauthorized Code - Attackers can inject and execute arbitrary code, potentially accessing sensitive data. |
| Integrity | Scope: 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.
Related CWEs
- 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
- MITRE Corporation. "CWE-1252: CPU Hardware Not Configured to Support Exclusivity of Write and Execute Operations." https://cwe.mitre.org/data/definitions/1252.html
- ARM. "Cortex-M MPU Configuration Guide"
- Intel. "Execute Disable Bit Functionality"