Insufficient Granularity of Address Regions Protected by Register Locks

Description

Insufficient Granularity of Address Regions Protected by Register Locks occurs when a hardware design protects address regions using register locks but with insufficient granularity, allowing unauthorized modification of protected memory. Security measures such as locks are often implemented to prevent unauthorized access to protected memory regions. However, when the granularity of these locks is larger than necessary—protecting broader regions than intended—parts of memory that should remain modifiable become locked, while insufficient coverage may leave gaps. Attackers may exploit these gaps to modify supposedly protected data.

Risk

Insufficient address protection granularity has severe security implications. Protected data may be accessible through gaps in coverage. Critical configuration may be modifiable outside locked regions. Security boundaries cannot be precisely enforced. Boot code protection may have exploitable gaps. Firmware integrity may be compromised. Key storage regions may be partially exposed. Access control policies may be circumvented. Sensitive data may leak through unprotected adjacent regions.

Solution

Implement lock mechanisms with granularity matching protection requirements. Use multiple lock regions to cover non-contiguous protected areas. Ensure no gaps exist between protected regions. Verify lock coverage through security testing. Document exactly which addresses are protected by each lock. Consider page-level or cache-line-level locking granularity. Implement hardware verification of lock coverage. Test boundary conditions around protected regions. Use defense in depth with overlapping protection mechanisms.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Modify Memory - Attackers can modify protected memory through gaps in lock coverage or by exploiting coarse granularity.
Access ControlScope: Access Control

Bypass Protection Mechanism - Insufficient granularity allows bypassing memory protection intended by locks.

Example Code

Vulnerable Code

// Vulnerable: Coarse-grained memory lock protection

module vulnerable_memory_lock (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire set_lock,
    output reg [31:0] read_data,
    output reg write_error
);

    // Memory array
    reg [31:0] memory [0:4095];  // 16KB memory

    // VULNERABLE: Single lock for entire memory
    // Cannot protect specific regions independently
    reg memory_locked;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            memory_locked <= 1'b0;
        end
        else if (set_lock) begin
            memory_locked <= 1'b1;  // Locks ALL memory
        end
    end

    always @(posedge clk) begin
        if (write_enable) begin
            if (memory_locked) begin
                // VULNERABLE: Everything locked - too coarse
                // Or nothing locked - no protection
                write_error <= 1'b1;
            end else begin
                memory[address[13:2]] <= write_data;
                write_error <= 1'b0;
            end
        end
    end

endmodule

// Vulnerable: Lock regions with gaps
module vulnerable_region_locks (
    input wire clk,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    output reg write_error
);

    // Memory regions
    // 0x0000-0x0FFF: Boot code (should be protected)
    // 0x1000-0x1FFF: Configuration (should be protected)
    // 0x2000-0x2FFF: Key storage (should be protected)
    // 0x3000-0x3FFF: User data (writable)

    // VULNERABLE: Lock regions don't align with data boundaries
    reg lock_region_0;  // Covers 0x0000-0x07FF (only half of boot code!)
    reg lock_region_1;  // Covers 0x0800-0x0FFF
    reg lock_region_2;  // Covers 0x1000-0x17FF
    // GAP: 0x1800-0x1FFF not covered by any lock!
    reg lock_region_3;  // Covers 0x2000-0x2FFF

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

        if (address < 32'h0800) begin
            if (lock_region_0) write_error = 1'b1;
        end
        else if (address < 32'h1000) begin
            if (lock_region_1) write_error = 1'b1;
        end
        else if (address < 32'h1800) begin
            if (lock_region_2) write_error = 1'b1;
        end
        // GAP: 0x1800-0x1FFF has NO lock protection!
        // Attacker can modify this region
        else if (address >= 32'h2000 && address < 32'h3000) begin
            if (lock_region_3) write_error = 1'b1;
        end
    end

endmodule
// Vulnerable: Software with insufficient lock granularity awareness

#define BOOT_CODE_START   0x0000
#define BOOT_CODE_END     0x0FFF
#define CONFIG_START      0x1000
#define CONFIG_END        0x1FFF
#define KEY_STORAGE_START 0x2000
#define KEY_STORAGE_END   0x2FFF

// Hardware lock regions (misaligned with data)
#define LOCK_REGION_SIZE  0x0800  // 2KB per lock

void vulnerable_setup_locks(void) {
    // VULNERABLE: Locks don't cover intended regions properly

    // This only locks half of boot code
    set_lock_region(0);  // Covers 0x0000-0x07FF
    set_lock_region(1);  // Covers 0x0800-0x0FFF

    // This only locks half of config
    set_lock_region(2);  // Covers 0x1000-0x17FF
    // Region 0x1800-0x1FFF is NOT LOCKED!

    set_lock_region(3);  // Covers 0x2000-0x27FF
    // Key storage 0x2800-0x2FFF is NOT LOCKED!
}

// Attacker can exploit gaps
void exploit_lock_gap(void) {
    // Configuration region is "locked" but...
    // Address 0x1800-0x1FFF has no lock!

    uint32_t* config_gap = (uint32_t*)0x1800;
    *config_gap = MALICIOUS_CONFIG;  // Works! No lock here

    // Key storage is "locked" but...
    // Address 0x2800-0x2FFF has no lock!

    uint32_t* key_gap = (uint32_t*)0x2800;
    *key_gap = 0;  // Can corrupt keys!
}

Fixed Code

// Fixed: Fine-grained memory lock protection

module secure_memory_lock (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire [15:0] set_lock_mask,
    input wire lock_enable,
    output reg [31:0] read_data,
    output reg write_error
);

    // Memory array
    reg [31:0] memory [0:4095];  // 16KB memory

    // Fine-grained locks: one per 1KB region
    reg [15:0] region_locks;  // 16 locks for 16KB

    // Region size in bytes
    localparam REGION_SIZE = 1024;  // 1KB per lock region
    localparam REGION_BITS = 10;

    wire [3:0] region_index = address[13:REGION_BITS];

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            region_locks <= 16'h0;
        end
        else if (lock_enable) begin
            // Can lock multiple regions atomically
            region_locks <= region_locks | set_lock_mask;
        end
    end

    always @(posedge clk) begin
        if (write_enable) begin
            // Check specific region lock
            if (region_locks[region_index]) begin
                write_error <= 1'b1;
            end else begin
                memory[address[13:2]] <= write_data;
                write_error <= 1'b0;
            end
        end
    end

endmodule

// Fixed: Configurable lock regions with complete coverage
module secure_region_locks (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire configure_enable,
    input wire [3:0] region_select,
    input wire [31:0] region_start_in,
    input wire [31:0] region_end_in,
    input wire lock_region,
    output reg write_error
);

    // Configurable protected regions
    parameter NUM_REGIONS = 8;

    reg [31:0] region_start [0:NUM_REGIONS-1];
    reg [31:0] region_end [0:NUM_REGIONS-1];
    reg [NUM_REGIONS-1:0] region_locked;
    reg configuration_locked;

    integer i;

    // Check if address falls within any locked region
    function is_address_locked;
        input [31:0] addr;
        integer j;
        begin
            is_address_locked = 1'b0;
            for (j = 0; j < NUM_REGIONS; j = j + 1) begin
                if (region_locked[j] &&
                    addr >= region_start[j] &&
                    addr <= region_end[j]) begin
                    is_address_locked = 1'b1;
                end
            end
        end
    endfunction

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            for (i = 0; i < NUM_REGIONS; i = i + 1) begin
                region_start[i] <= 32'hFFFFFFFF;
                region_end[i] <= 32'h00000000;
                region_locked[i] <= 1'b0;
            end
            configuration_locked <= 1'b0;
        end
        else if (configure_enable && !configuration_locked) begin
            // Configure region boundaries
            region_start[region_select] <= region_start_in;
            region_end[region_select] <= region_end_in;

            if (lock_region) begin
                region_locked[region_select] <= 1'b1;
            end
        end
    end

    always @(*) begin
        write_error = is_address_locked(address);
    end

endmodule

// Fixed: Hierarchical lock system with guaranteed coverage
module secure_hierarchical_locks (
    input wire clk,
    input wire reset_n,
    input wire [31:0] address,
    input wire write_enable,
    output reg write_error
);

    // Hierarchical locks for complete coverage
    // Level 0: Coarse (4KB regions)
    // Level 1: Medium (1KB regions)
    // Level 2: Fine (256B regions)

    reg [3:0] coarse_locks;      // 4 x 4KB = 16KB
    reg [15:0] medium_locks;     // 16 x 1KB = 16KB
    reg [63:0] fine_locks;       // 64 x 256B = 16KB

    wire [1:0] coarse_index = address[13:12];
    wire [3:0] medium_index = address[13:10];
    wire [5:0] fine_index = address[13:8];

    // Address is locked if ANY level locks it
    wire coarse_locked = coarse_locks[coarse_index];
    wire medium_locked = medium_locks[medium_index];
    wire fine_locked = fine_locks[fine_index];

    always @(*) begin
        // Hierarchical check - any lock blocks access
        write_error = coarse_locked | medium_locked | fine_locked;
    end

endmodule
// Fixed: Software with proper lock granularity

#define BOOT_CODE_START   0x0000
#define BOOT_CODE_END     0x0FFF
#define CONFIG_START      0x1000
#define CONFIG_END        0x1FFF
#define KEY_STORAGE_START 0x2000
#define KEY_STORAGE_END   0x2FFF

// Fine-grained lock control
#define LOCK_REGION_SIZE  0x0400  // 1KB per lock - matches data boundaries

void secure_setup_locks(void) {
    // Configure locks to exactly match protected regions

    // Boot code: 0x0000-0x0FFF (4 lock regions)
    configure_lock_region(0, BOOT_CODE_START, BOOT_CODE_START + 0x3FF);
    configure_lock_region(1, BOOT_CODE_START + 0x400, BOOT_CODE_START + 0x7FF);
    configure_lock_region(2, BOOT_CODE_START + 0x800, BOOT_CODE_START + 0xBFF);
    configure_lock_region(3, BOOT_CODE_START + 0xC00, BOOT_CODE_END);

    // Configuration: 0x1000-0x1FFF (4 lock regions)
    configure_lock_region(4, CONFIG_START, CONFIG_START + 0x3FF);
    configure_lock_region(5, CONFIG_START + 0x400, CONFIG_START + 0x7FF);
    configure_lock_region(6, CONFIG_START + 0x800, CONFIG_START + 0xBFF);
    configure_lock_region(7, CONFIG_START + 0xC00, CONFIG_END);

    // Key storage: 0x2000-0x2FFF (4 lock regions)
    configure_lock_region(8, KEY_STORAGE_START, KEY_STORAGE_START + 0x3FF);
    configure_lock_region(9, KEY_STORAGE_START + 0x400, KEY_STORAGE_START + 0x7FF);
    configure_lock_region(10, KEY_STORAGE_START + 0x800, KEY_STORAGE_START + 0xBFF);
    configure_lock_region(11, KEY_STORAGE_START + 0xC00, KEY_STORAGE_END);

    // Activate all configured locks
    for (int i = 0; i <= 11; i++) {
        activate_lock_region(i);
    }

    // Verify complete coverage
    verify_lock_coverage();
}

// Verification function
bool verify_lock_coverage(void) {
    // Check that all protected regions are fully covered

    for (uint32_t addr = BOOT_CODE_START; addr <= BOOT_CODE_END; addr += 4) {
        if (!is_address_locked(addr)) {
            log_error("Boot code gap at 0x%08X", addr);
            return false;
        }
    }

    for (uint32_t addr = CONFIG_START; addr <= CONFIG_END; addr += 4) {
        if (!is_address_locked(addr)) {
            log_error("Config gap at 0x%08X", addr);
            return false;
        }
    }

    for (uint32_t addr = KEY_STORAGE_START; addr <= KEY_STORAGE_END; addr += 4) {
        if (!is_address_locked(addr)) {
            log_error("Key storage gap at 0x%08X", addr);
            return false;
        }
    }

    // Verify user region is NOT locked
    for (uint32_t addr = 0x3000; addr <= 0x3FFF; addr += 4) {
        if (is_address_locked(addr)) {
            log_error("User region incorrectly locked at 0x%08X", addr);
            return false;
        }
    }

    return true;
}

CVE Examples

Address region lock granularity issues have been found in various hardware designs where protected regions had gaps or misaligned boundaries.


  • CWE-1220: Insufficient Granularity of Access Control (related)
  • CWE-1222: Insufficient Granularity of Address Regions (parent concept)
  • CWE-1198: Privilege Separation and Access Control Issues (category member)

References

  1. MITRE Corporation. "CWE-1222: Insufficient Granularity of Address Regions Protected by Register Locks." https://cwe.mitre.org/data/definitions/1222.html
  2. Memory Protection Unit Design Guidelines
  3. Hardware Lock Implementation Best Practices