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

ImpactDetails
Access ControlScope: Access Control

Bypass Protection Mechanism - Secure boot can be bypassed through memory modification.
IntegrityScope: Integrity

Modify Memory - Boot code can be altered after authentication.
AuthorizationScope: Authorization

Execute Unauthorized Code - Malicious boot code can be executed.
AuthenticationScope: 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.

  • 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

  1. MITRE Corporation. "CWE-1274: Improper Access Control for Volatile Memory Containing Boot Code." https://cwe.mitre.org/data/definitions/1274.html
  2. ARM. "TrustZone Security for Boot"
  3. NIST. "Secure Boot Guidelines"