Improper Handling of Overlap Between Protected Memory Ranges

Description

Improper Handling of Overlap Between Protected Memory Ranges occurs when a product allows address regions to overlap, which can result in the bypassing of intended memory protection. This weakness occurs in hardware designs using isolated memory regions and access control policies to protect privileged software. When lower-privilege software can program overlapping memory address regions with higher-privilege regions, attackers may achieve privilege escalation. The memory protection unit (MPU) may incorrectly handle these overlaps, enabling unauthorized reads/writes or denial-of-service attacks.

Risk

Overlapping memory ranges create severe security implications. Memory protection can be bypassed. Privilege escalation becomes possible. Protected data may be read or modified. Kernel memory may be accessible to user code. Secure enclaves may be compromised. Denial of service through memory access blocking. System stability may be affected. Security isolation boundaries are violated.

Solution

Ensure memory regions remain properly isolated with enforced access control policies. Define a priority scheme for overlapping regions (e.g., lowest ID takes precedence). Block programming of regions with overlapping addresses via hardware logic. Apply policy filters to all overlapping ranges—access granted only if all policies permit it. Validate region configurations before applying them.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - Protected memory contents can be read through overlapping region bypass.
IntegrityScope: Integrity

Modify Memory - Protected memory can be modified through overlapping regions.
AvailabilityScope: Availability

DoS: Instability - Overlapping regions can block legitimate access or cause system instability.

Example Code

Vulnerable Code

// Vulnerable: MPU without overlap protection

module vulnerable_mpu (
    input wire clk,
    input wire reset_n,
    // Region configuration
    input wire [2:0] config_region_id,
    input wire [31:0] config_base_addr,
    input wire [31:0] config_size,
    input wire [3:0] config_permissions,
    input wire config_enable,
    input wire config_write,
    // Memory access
    input wire [31:0] access_addr,
    input wire [1:0] access_privilege,  // 0=user, 1=supervisor, 2=hypervisor, 3=secure
    input wire access_read,
    input wire access_write,
    output reg access_allowed
);

    // 8 configurable regions
    reg [31:0] region_base [0:7];
    reg [31:0] region_size [0:7];
    reg [3:0] region_perms [0:7];  // [3:2]=write level, [1:0]=read level
    reg region_enabled [0:7];

    // VULNERABLE: Region configuration without overlap check
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            integer i;
            for (i = 0; i < 8; i = i + 1) begin
                region_enabled[i] <= 1'b0;
            end
        end
        else if (config_write && config_enable) begin
            // VULNERABLE: No check if new region overlaps existing regions
            region_base[config_region_id] <= config_base_addr;
            region_size[config_region_id] <= config_size;
            region_perms[config_region_id] <= config_permissions;
            region_enabled[config_region_id] <= 1'b1;
        end
    end

    // Access check
    reg [7:0] region_match;
    reg access_result;

    always @(*) begin
        integer i;
        access_allowed = 1'b0;

        for (i = 0; i < 8; i = i + 1) begin
            if (region_enabled[i] &&
                access_addr >= region_base[i] &&
                access_addr < (region_base[i] + region_size[i])) begin

                // VULNERABLE: First matching region wins
                // Attacker can create overlapping region with higher ID
                // and more permissive settings

                if (access_read && access_privilege >= region_perms[i][1:0]) begin
                    access_allowed = 1'b1;
                end
                if (access_write && access_privilege >= region_perms[i][3:2]) begin
                    access_allowed = 1'b1;
                end
            end
        end
    end

    // Attack:
    // Region 0: base=0x8000_0000, size=0x1000, perms=0xF (secure only)
    // Attacker programs Region 7: base=0x8000_0000, size=0x1000, perms=0x0 (all access)
    // Both regions match, but attacker's permissive region allows access

endmodule

// Vulnerable: Memory map with overlap vulnerability
module vulnerable_memory_map (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire read_enable,
    input wire write_enable,
    input wire [1:0] privilege_level,
    output reg [31:0] read_data,
    output reg access_denied
);

    // Device regions
    // VULNERABLE: UART region overlaps with AES region

    parameter UART_BASE = 32'h1000_0000;
    parameter UART_SIZE = 32'h0011_1000;  // Too large!
    // UART ends at 0x1011_1000

    parameter AES_BASE = 32'h1010_0000;   // Overlaps with UART!
    parameter AES_SIZE = 32'h0001_0000;
    // AES: 0x1010_0000 - 0x1010_FFFF
    // This is INSIDE the UART range!

    wire in_uart_range = (addr >= UART_BASE) && (addr < UART_BASE + UART_SIZE);
    wire in_aes_range = (addr >= AES_BASE) && (addr < AES_BASE + AES_SIZE);

    // UART: accessible to all (privilege >= 0)
    // AES: requires secure access (privilege >= 3)

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

        if (in_uart_range && in_aes_range) begin
            // VULNERABLE: Both ranges match!
            // Which policy applies? Implementation picks UART (permissive)
            access_denied = 1'b0;  // UART policy: allow all
        end
        else if (in_aes_range) begin
            if (privilege_level < 2'd3) begin
                access_denied = 1'b1;
            end
        end
        // UART range: always allowed
    end

    // Attack: Access AES at 0x1010_0000 through UART region
    // Address is in both ranges, but UART policy allows access

endmodule
// Vulnerable: Software MPU configuration with overlap

#include <stdint.h>

#define MPU_REGIONS 8

typedef struct {
    uint32_t base;
    uint32_t size;
    uint32_t permissions;
    uint32_t enabled;
} mpu_region_t;

mpu_region_t mpu_regions[MPU_REGIONS];

// VULNERABLE: No overlap checking
int vulnerable_configure_region(int region_id, uint32_t base, uint32_t size, uint32_t perms) {
    if (region_id >= MPU_REGIONS) {
        return -EINVAL;
    }

    // VULNERABLE: No check for overlap with existing regions
    mpu_regions[region_id].base = base;
    mpu_regions[region_id].size = size;
    mpu_regions[region_id].permissions = perms;
    mpu_regions[region_id].enabled = 1;

    // Apply to hardware
    MPU->RNR = region_id;
    MPU->RBAR = base;
    MPU->RASR = (size << 1) | perms | MPU_RASR_ENABLE_Msk;

    return 0;
}

// Attack scenario
void exploit_overlap(void) {
    // Kernel sets up protected region
    // Region 0: 0x20000000-0x20001000, supervisor only

    // Attacker (with privilege to configure MPU) creates overlap
    // Region 7: 0x20000000-0x20001000, all access

    // Now both regions cover same address
    // Access check may use attacker's permissive region
}

Fixed Code

// Fixed: MPU with overlap protection

module secure_mpu (
    input wire clk,
    input wire reset_n,
    // Region configuration
    input wire [2:0] config_region_id,
    input wire [31:0] config_base_addr,
    input wire [31:0] config_size,
    input wire [3:0] config_permissions,
    input wire config_enable,
    input wire config_write,
    input wire [1:0] config_privilege,  // Configuring agent's privilege
    // Memory access
    input wire [31:0] access_addr,
    input wire [1:0] access_privilege,
    input wire access_read,
    input wire access_write,
    output reg access_allowed,
    output reg config_denied,
    output reg overlap_detected
);

    // 8 configurable regions
    reg [31:0] region_base [0:7];
    reg [31:0] region_size [0:7];
    reg [3:0] region_perms [0:7];
    reg region_enabled [0:7];
    reg [1:0] region_owner [0:7];  // Privilege level that configured region

    // FIXED: Check for overlap before allowing configuration
    function automatic overlaps_existing;
        input [31:0] new_base;
        input [31:0] new_size;
        input [2:0] new_id;
        integer i;
        reg [31:0] new_end;
        reg [31:0] existing_end;
        begin
            overlaps_existing = 1'b0;
            new_end = new_base + new_size;

            for (i = 0; i < 8; i = i + 1) begin
                if (i != new_id && region_enabled[i]) begin
                    existing_end = region_base[i] + region_size[i];

                    // Check for any overlap
                    if (!(new_end <= region_base[i] || new_base >= existing_end)) begin
                        overlaps_existing = 1'b1;
                    end
                end
            end
        end
    endfunction

    // FIXED: Detect overlaps and require higher privilege
    wire proposed_overlaps = overlaps_existing(config_base_addr, config_size, config_region_id);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            integer i;
            for (i = 0; i < 8; i = i + 1) begin
                region_enabled[i] <= 1'b0;
            end
            config_denied <= 1'b0;
            overlap_detected <= 1'b0;
        end
        else if (config_write && config_enable) begin
            config_denied <= 1'b0;
            overlap_detected <= 1'b0;

            // FIXED: Check for overlap
            if (proposed_overlaps) begin
                overlap_detected <= 1'b1;
                // FIXED: Only allow overlap if configuring agent has highest privilege
                if (config_privilege < 2'd3) begin
                    config_denied <= 1'b1;
                end
                else begin
                    // Secure master can create overlaps
                    region_base[config_region_id] <= config_base_addr;
                    region_size[config_region_id] <= config_size;
                    region_perms[config_region_id] <= config_permissions;
                    region_enabled[config_region_id] <= 1'b1;
                    region_owner[config_region_id] <= config_privilege;
                end
            end
            else begin
                // No overlap - allow configuration
                region_base[config_region_id] <= config_base_addr;
                region_size[config_region_id] <= config_size;
                region_perms[config_region_id] <= config_permissions;
                region_enabled[config_region_id] <= 1'b1;
                region_owner[config_region_id] <= config_privilege;
            end
        end
    end

    // FIXED: Access check with priority - most restrictive wins
    always @(*) begin
        integer i;
        reg [3:0] required_read_level;
        reg [3:0] required_write_level;
        reg any_match;

        access_allowed = 1'b0;
        required_read_level = 4'd0;
        required_write_level = 4'd0;
        any_match = 1'b0;

        for (i = 0; i < 8; i = i + 1) begin
            if (region_enabled[i] &&
                access_addr >= region_base[i] &&
                access_addr < (region_base[i] + region_size[i])) begin

                any_match = 1'b1;

                // FIXED: Take MOST restrictive permission from all matching regions
                if (region_perms[i][1:0] > required_read_level) begin
                    required_read_level = region_perms[i][1:0];
                end
                if (region_perms[i][3:2] > required_write_level) begin
                    required_write_level = region_perms[i][3:2];
                end
            end
        end

        // FIXED: Access only if privilege meets ALL requirements
        if (any_match) begin
            if (access_read && access_privilege >= required_read_level) begin
                access_allowed = 1'b1;
            end
            else if (access_write && access_privilege >= required_write_level) begin
                access_allowed = 1'b1;
            end
            else begin
                access_allowed = 1'b0;
            end
        end
    end

endmodule

// Fixed: Memory map without overlap
module secure_memory_map (
    input wire clk,
    input wire reset_n,
    input wire [31:0] addr,
    input wire read_enable,
    input wire write_enable,
    input wire [1:0] privilege_level,
    output reg [31:0] read_data,
    output reg access_denied
);

    // FIXED: Non-overlapping device regions
    parameter UART_BASE = 32'h1000_0000;
    parameter UART_SIZE = 32'h0000_1000;  // Fixed: 4KB only
    // UART: 0x1000_0000 - 0x1000_0FFF

    parameter AES_BASE = 32'h1010_0000;
    parameter AES_SIZE = 32'h0001_0000;
    // AES: 0x1010_0000 - 0x1010_FFFF

    // FIXED: Verify no overlap at compile time
    // UART ends at 0x1000_1000, AES starts at 0x1010_0000 - no overlap

    wire in_uart_range = (addr >= UART_BASE) && (addr < UART_BASE + UART_SIZE);
    wire in_aes_range = (addr >= AES_BASE) && (addr < AES_BASE + AES_SIZE);

    // FIXED: Detect if somehow both match (should not happen)
    wire overlap_error = in_uart_range && in_aes_range;

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

        if (overlap_error) begin
            // FIXED: Fail-secure on overlap
            access_denied = 1'b1;
        end
        else if (in_aes_range) begin
            // AES requires secure access
            if (privilege_level < 2'd3) begin
                access_denied = 1'b1;
            end
        end
        else if (in_uart_range) begin
            // UART accessible to all
            access_denied = 1'b0;
        end
        else begin
            // Unknown region
            access_denied = 1'b1;
        end
    end

endmodule
// Fixed: Software MPU configuration with overlap detection

#include <stdint.h>

#define MPU_REGIONS 8

typedef struct {
    uint32_t base;
    uint32_t size;
    uint32_t permissions;
    uint32_t enabled;
    uint32_t owner_privilege;
} mpu_region_t;

static mpu_region_t mpu_regions[MPU_REGIONS];

// FIXED: Check for overlap with existing regions
static bool check_overlap(int region_id, uint32_t base, uint32_t size) {
    uint32_t new_end = base + size;

    for (int i = 0; i < MPU_REGIONS; i++) {
        if (i != region_id && mpu_regions[i].enabled) {
            uint32_t existing_end = mpu_regions[i].base + mpu_regions[i].size;

            // Check for any overlap
            if (!(new_end <= mpu_regions[i].base || base >= existing_end)) {
                return true;  // Overlap detected
            }
        }
    }
    return false;
}

// FIXED: Configure region with overlap protection
int secure_configure_region(int region_id, uint32_t base, uint32_t size,
                           uint32_t perms, uint32_t caller_privilege) {
    if (region_id >= MPU_REGIONS) {
        return -EINVAL;
    }

    // FIXED: Validate alignment (size must be power of 2)
    if ((size & (size - 1)) != 0) {
        return -EINVAL;
    }

    // FIXED: Base must be aligned to size
    if ((base & (size - 1)) != 0) {
        return -EINVAL;
    }

    // FIXED: Check for overlap
    if (check_overlap(region_id, base, size)) {
        // FIXED: Only secure master can create overlapping regions
        if (caller_privilege < PRIVILEGE_SECURE) {
            log_security_event("Overlap attempt denied: region %d", region_id);
            return -EPERM;
        }

        // Secure master creating overlap - apply most restrictive policy
        log_security_event("Secure overlap created: region %d", region_id);
    }

    // FIXED: Cannot configure region owned by higher privilege
    if (mpu_regions[region_id].enabled &&
        mpu_regions[region_id].owner_privilege > caller_privilege) {
        return -EPERM;
    }

    // Configure region
    mpu_regions[region_id].base = base;
    mpu_regions[region_id].size = size;
    mpu_regions[region_id].permissions = perms;
    mpu_regions[region_id].enabled = 1;
    mpu_regions[region_id].owner_privilege = caller_privilege;

    // Apply to hardware
    __disable_irq();
    MPU->RNR = region_id;
    MPU->RBAR = base;
    MPU->RASR = encode_size(size) | perms | MPU_RASR_ENABLE_Msk;
    __DSB();
    __ISB();
    __enable_irq();

    return 0;
}

// FIXED: Access check considering all matching regions
bool secure_check_access(uint32_t addr, uint32_t privilege, bool write) {
    uint32_t required_privilege = 0;
    bool any_match = false;

    // FIXED: Find most restrictive requirement from all matching regions
    for (int i = 0; i < MPU_REGIONS; i++) {
        if (mpu_regions[i].enabled) {
            uint32_t region_end = mpu_regions[i].base + mpu_regions[i].size;

            if (addr >= mpu_regions[i].base && addr < region_end) {
                any_match = true;

                uint32_t region_req = write ?
                    (mpu_regions[i].permissions >> 4) & 0xF :
                    mpu_regions[i].permissions & 0xF;

                // Take most restrictive
                if (region_req > required_privilege) {
                    required_privilege = region_req;
                }
            }
        }
    }

    if (!any_match) {
        return false;  // No region covers this address
    }

    return privilege >= required_privilege;
}

CVE Examples

  • CVE-2008-7096: Virtualization product bypass via remapping registers that overlapped with protected memory
  • Processor design flaws allowing ring 0 code to access higher privilege rings through register window overlap

  • CWE-284: Improper Access Control (parent)
  • CWE-119: Improper Restriction of Operations within Memory Buffer Bounds (can precede)
  • CWE-1257: Improper Access Control Applied to Mirrored or Aliased Memory Regions (related)

References

  1. MITRE Corporation. "CWE-1260: Improper Handling of Overlap Between Protected Memory Ranges." https://cwe.mitre.org/data/definitions/1260.html
  2. ARM. "Memory Protection Unit User Guide"
  3. Intel. "System Programming Guide" - Memory Protection