Improper Access Control for Volatile Memory Containing Boot Code
Description
Improper Access Control for Volatile Memory Containing Boot Code occurs when a secure-boot process transfers bootloader code from Non-Volatile Memory (NVM) to Volatile Memory (VM) without sufficient access controls. During secure-boot, ROM code in a System-on-Chip fetches bootloader code from external NVM and stores it in internal VM (DRAM/SRAM). While the code undergoes authentication during transfer, the resulting volatile memory lacks adequate protections against modification. Adversaries could bypass the secure-boot process and execute their own untrusted, malicious boot code.
Risk
Improper volatile memory protection has severe security implications. Boot code can be modified after authentication. Secure boot can be completely bypassed. Malicious code can be executed. Privilege escalation becomes possible. System integrity is compromised. Trust chain is broken. Firmware rootkits can be installed. Device may become permanently compromised.
Solution
Design volatile-memory protections preventing adversary modifications after boot code is loaded. Test protections thoroughly against unauthorized access attempts. Verify volatile memory is lockable and properly locked during boot. Test write attempts from untrusted agents to ensure rejection. Map fabric master agents active during code loading and verify access restrictions.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Bypass Protection Mechanism - Secure boot can be bypassed through memory modification. |
| Integrity | Scope: Integrity Modify Memory - Boot code can be altered after authentication. |
| Authorization | Scope: Authorization Execute Unauthorized Code - Malicious boot code can be executed. |
| Authentication | Scope: Authentication Gain Privileges - Attackers can gain system-level privileges. |
Example Code
Vulnerable Code
// Vulnerable: Boot code memory without access control
module vulnerable_boot_memory (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
input wire [3:0] master_id,
output reg [31:0] read_data,
// Boot control
input wire boot_complete,
input wire signature_verified
);
// Boot code memory region
reg [31:0] boot_memory [0:4095];
// Memory range for boot code
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
read_data <= 32'h0;
end
else begin
// VULNERABLE: No access control after boot code loaded
// Any master can write to boot memory
if (write_enable && is_boot_region) begin
// VULNERABLE: Write allowed even after signature verification
boot_memory[address[13:2]] <= write_data;
// Problem: DMA controller (master_id = 4) can overwrite
// boot code after it was authenticated
end
if (read_enable && is_boot_region) begin
read_data <= boot_memory[address[13:2]];
end
end
end
endmodule
// Vulnerable: Boot loader without memory locking
module vulnerable_boot_loader (
input wire clk,
input wire reset_n,
// NVM interface
output reg [31:0] nvm_addr,
input wire [31:0] nvm_data,
output reg nvm_read,
// VM interface
output reg [31:0] vm_addr,
output reg [31:0] vm_data,
output reg vm_write,
// Control
input wire start_boot,
output reg boot_done,
output reg signature_valid
);
reg [15:0] byte_count;
reg [2:0] state;
parameter IDLE = 3'd0;
parameter LOAD = 3'd1;
parameter VERIFY = 3'd2;
parameter EXECUTE = 3'd3;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
boot_done <= 1'b0;
signature_valid <= 1'b0;
end
else begin
case (state)
IDLE: begin
if (start_boot) begin
state <= LOAD;
byte_count <= 16'h0;
end
end
LOAD: begin
// Load boot code from NVM to VM
nvm_addr <= byte_count;
nvm_read <= 1'b1;
vm_addr <= byte_count;
vm_data <= nvm_data;
vm_write <= 1'b1;
byte_count <= byte_count + 4;
if (byte_count >= 16'hFFF0) begin
state <= VERIFY;
end
end
VERIFY: begin
// Verify signature
if (verify_signature()) begin
signature_valid <= 1'b1;
state <= EXECUTE;
end
else begin
state <= IDLE;
end
end
EXECUTE: begin
// VULNERABLE: No memory lock before execute
// Boot code can still be modified!
boot_done <= 1'b1;
// Execute boot code...
end
endcase
end
end
endmodule
// Vulnerable: Software boot without memory protection
#include <stdint.h>
#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000
// VULNERABLE: No memory protection after loading boot code
void vulnerable_load_boot_code(void) {
uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
uint32_t* nvm = (uint32_t*)NVM_BASE;
// Copy boot code from NVM to volatile memory
for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
boot_mem[i] = nvm[i];
}
// Verify signature
if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
halt();
return;
}
// VULNERABLE: No memory protection enabled
// Boot code can be modified between verification and execution
// Execute boot code
void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
boot_entry();
// Attack: DMA or another master modifies boot_mem after signature
// verification but before execution
}
// VULNERABLE: MPU not configured for boot memory
void vulnerable_mpu_setup(void) {
// Configure MPU for other regions
configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW);
configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW);
// VULNERABLE: Boot memory region not protected
// No MPU region configured for BOOT_MEMORY_BASE
// Any code can modify boot memory!
}
Fixed Code
// Fixed: Boot code memory with proper access control
module secure_boot_memory (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
input wire [3:0] master_id,
output reg [31:0] read_data,
output reg access_denied,
// Boot control
input wire boot_loading, // Boot code being loaded
input wire signature_verified, // Signature check passed
input wire boot_complete, // Boot process complete
// Lock control
input wire lock_boot_memory, // Lock command
output reg boot_memory_locked
);
// Boot code memory region
reg [31:0] boot_memory [0:4095];
// Memory range for boot code
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
// Trusted masters
parameter BOOT_ROM = 4'd0;
parameter SECURE_DMA = 4'd1;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
wire is_trusted_master = (master_id == BOOT_ROM) || (master_id == SECURE_DMA);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
read_data <= 32'h0;
access_denied <= 1'b0;
boot_memory_locked <= 1'b0;
end
else begin
access_denied <= 1'b0;
// FIXED: Lock boot memory after signature verification
if (lock_boot_memory || signature_verified) begin
boot_memory_locked <= 1'b1;
end
if (write_enable && is_boot_region) begin
// FIXED: Check access permissions
if (boot_memory_locked) begin
// Memory locked - deny all writes
access_denied <= 1'b1;
end
else if (boot_loading && is_trusted_master) begin
// Only allow writes during loading from trusted masters
boot_memory[address[13:2]] <= write_data;
end
else begin
access_denied <= 1'b1;
end
end
if (read_enable && is_boot_region) begin
// FIXED: Read access controlled
if (is_trusted_master || boot_complete) begin
read_data <= boot_memory[address[13:2]];
end
else begin
read_data <= 32'h0;
access_denied <= 1'b1;
end
end
end
end
endmodule
// Fixed: Boot loader with memory locking
module secure_boot_loader (
input wire clk,
input wire reset_n,
// NVM interface
output reg [31:0] nvm_addr,
input wire [31:0] nvm_data,
output reg nvm_read,
// VM interface
output reg [31:0] vm_addr,
output reg [31:0] vm_data,
output reg vm_write,
// Memory protection
output reg lock_boot_memory,
input wire memory_locked,
// Control
input wire start_boot,
output reg boot_done,
output reg signature_valid,
output reg boot_error
);
reg [15:0] byte_count;
reg [3:0] state;
parameter IDLE = 4'd0;
parameter LOAD = 4'd1;
parameter VERIFY = 4'd2;
parameter LOCK = 4'd3;
parameter VERIFY_LOCK = 4'd4;
parameter EXECUTE = 4'd5;
parameter ERROR = 4'd6;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= IDLE;
boot_done <= 1'b0;
signature_valid <= 1'b0;
lock_boot_memory <= 1'b0;
boot_error <= 1'b0;
end
else begin
case (state)
IDLE: begin
boot_error <= 1'b0;
if (start_boot) begin
state <= LOAD;
byte_count <= 16'h0;
end
end
LOAD: begin
// Load boot code from NVM to VM
nvm_addr <= byte_count;
nvm_read <= 1'b1;
vm_addr <= byte_count;
vm_data <= nvm_data;
vm_write <= 1'b1;
byte_count <= byte_count + 4;
if (byte_count >= 16'hFFF0) begin
state <= VERIFY;
end
end
VERIFY: begin
vm_write <= 1'b0;
// Verify signature
if (verify_signature()) begin
signature_valid <= 1'b1;
state <= LOCK;
end
else begin
state <= ERROR;
end
end
LOCK: begin
// FIXED: Lock boot memory before execution
lock_boot_memory <= 1'b1;
state <= VERIFY_LOCK;
end
VERIFY_LOCK: begin
// FIXED: Verify memory is actually locked
if (memory_locked) begin
state <= EXECUTE;
end
else begin
// Lock failed - abort
state <= ERROR;
end
end
EXECUTE: begin
// FIXED: Only execute after memory is locked
boot_done <= 1'b1;
// Execute boot code safely
end
ERROR: begin
boot_error <= 1'b1;
// Halt or retry
end
endcase
end
end
endmodule
// Fixed: Memory protection unit for boot code
module boot_memory_protection (
input wire clk,
input wire reset_n,
input wire [31:0] address,
input wire [3:0] master_id,
input wire access_type, // 0=read, 1=write
input wire boot_phase_complete,
input wire lock_enable,
output reg access_allowed,
output reg protection_violation
);
parameter BOOT_START = 32'h0000_0000;
parameter BOOT_END = 32'h0000_FFFF;
parameter BOOT_ROM = 4'd0;
reg memory_locked;
wire is_boot_region = (address >= BOOT_START) && (address <= BOOT_END);
wire is_boot_rom = (master_id == BOOT_ROM);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
memory_locked <= 1'b0;
access_allowed <= 1'b0;
protection_violation <= 1'b0;
end
else begin
protection_violation <= 1'b0;
// FIXED: Lock memory permanently after boot
if (lock_enable || boot_phase_complete) begin
memory_locked <= 1'b1;
end
// FIXED: Access control logic
if (is_boot_region) begin
if (access_type == 1'b1) begin // Write access
if (memory_locked) begin
// FIXED: No writes after lock
access_allowed <= 1'b0;
protection_violation <= 1'b1;
end
else if (is_boot_rom) begin
// Only boot ROM can write before lock
access_allowed <= 1'b1;
end
else begin
access_allowed <= 1'b0;
protection_violation <= 1'b1;
end
end
else begin // Read access
// Read allowed after boot complete
access_allowed <= boot_phase_complete || is_boot_rom;
end
end
else begin
access_allowed <= 1'b1; // Non-boot regions
end
end
end
endmodule
// Fixed: Software boot with proper memory protection
#include <stdint.h>
#include <stdbool.h>
#define BOOT_MEMORY_BASE 0x20000000
#define BOOT_MEMORY_SIZE 0x10000
#define NVM_BASE 0x10000000
// MPU region for boot memory
#define MPU_BOOT_REGION 2
// FIXED: Lock boot memory with MPU
static void lock_boot_memory(void) {
// Configure MPU region for boot memory as read-only
configure_mpu_region(
MPU_BOOT_REGION,
BOOT_MEMORY_BASE,
BOOT_MEMORY_SIZE,
MPU_RO | MPU_XN_CLEAR // Read-only, execute permitted
);
// Enable MPU
enable_mpu();
// Memory barrier to ensure protection is active
__asm__ volatile("dmb sy" ::: "memory");
__asm__ volatile("isb" ::: "memory");
}
// FIXED: Verify boot memory is protected
static bool verify_boot_memory_locked(void) {
// Try to write to boot memory (should fail)
volatile uint32_t* test_addr = (volatile uint32_t*)BOOT_MEMORY_BASE;
uint32_t original = *test_addr;
// Temporarily disable fault handler to catch write attempt
uint32_t fault_occurred = 0;
set_mem_fault_handler(&fault_occurred);
// Attempt write
*test_addr = 0xDEADBEEF;
// Restore fault handler
restore_mem_fault_handler();
// Verify write was blocked
if (!fault_occurred || *test_addr != original) {
return false; // Protection not working
}
return true;
}
// FIXED: Secure boot with memory protection
void secure_load_boot_code(void) {
uint32_t* boot_mem = (uint32_t*)BOOT_MEMORY_BASE;
uint32_t* nvm = (uint32_t*)NVM_BASE;
// Copy boot code from NVM to volatile memory
for (int i = 0; i < BOOT_MEMORY_SIZE/4; i++) {
boot_mem[i] = nvm[i];
}
// Memory barrier before verification
__asm__ volatile("dmb sy" ::: "memory");
// Verify signature
if (!verify_boot_signature(boot_mem, BOOT_MEMORY_SIZE)) {
log_error("Boot signature verification failed");
secure_halt();
return;
}
// FIXED: Lock boot memory before execution
lock_boot_memory();
// FIXED: Verify protection is active
if (!verify_boot_memory_locked()) {
log_error("Failed to lock boot memory");
secure_halt();
return;
}
// FIXED: Memory barrier before execution
__asm__ volatile("dmb sy" ::: "memory");
__asm__ volatile("isb" ::: "memory");
// Now safe to execute - memory is protected
void (*boot_entry)(void) = (void(*)(void))BOOT_MEMORY_BASE;
boot_entry();
}
// FIXED: Comprehensive MPU setup
void secure_mpu_setup(void) {
// Disable MPU during configuration
disable_mpu();
// Configure regions
configure_mpu_region(0, PERIPHERAL_BASE, PERIPHERAL_SIZE, MPU_RW | MPU_XN);
configure_mpu_region(1, RAM_BASE, RAM_SIZE, MPU_RW | MPU_XN);
// FIXED: Boot memory region - initially RW for loading
configure_mpu_region(
MPU_BOOT_REGION,
BOOT_MEMORY_BASE,
BOOT_MEMORY_SIZE,
MPU_RW | MPU_XN_CLEAR // RW during load, will be changed to RO
);
// Enable MPU with default deny
enable_mpu_with_default_deny();
}
CVE Examples
- CVE-2019-2267: Locked memory regions may be modified through other interfaces in a secure-boot-loader image due to improper access control, allowing secure boot bypass.
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1196: Security Flow Issues (category)
- CWE-693: Protection Mechanism Failure (related)
- CAPEC-456: Infected Memory (attack pattern)
- CAPEC-679: Exploitation of Improperly Configured Memory Protections (attack pattern)
References
- MITRE Corporation. "CWE-1274: Improper Access Control for Volatile Memory Containing Boot Code." https://cwe.mitre.org/data/definitions/1274.html
- ARM. "TrustZone Security for Boot"
- NIST. "Secure Boot Guidelines"