Fabric-Address Map Allows Programming of Unwarranted Overlaps of Protected and Unprotected Ranges

Description

Fabric-Address Map Allows Programming of Unwarranted Overlaps of Protected and Unprotected Ranges occurs when the on-chip fabric's address map contains overlapping protected and unprotected regions, enabling attackers to circumvent access controls targeting the overlapped portion of protected memory. Address ranges in memory or Memory-Mapped-IO (MMIO) spaces are defined via range registers containing base address and size information. When protection and unprotection zones intersect—either through design errors or malicious configuration—attackers can access protected data through the unprotected alias.

Risk

Address map overlaps have severe implications. Access control completely bypassed. Protected memory accessible via unprotected range. Isolation guarantees compromised. Confidential data leaked. Memory corruption possible. Security boundaries violated. DMA attacks enabled through overlaps. Virtualization protection bypassed. Medium likelihood when dynamic remapping is supported or range validation is missing.

Solution

Ensure protected and unprotected ranges remain isolated without overlap in chip address mapping and hardcoded RTL ranges during architecture and design phase. Prevent firmware-configured range overlaps during implementation. If overlaps are mandatory due to hardware constraints, ensure no sensitive assets occupy overlapped regions. Validate mitigations through comprehensive testing. Implement overlap detection logic in hardware.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Protected data readable through unprotected overlapping address range.
IntegrityScope: Integrity

Memory modification via the unprotected portion of overlapped regions.
Access ControlScope: Access Control

Protection mechanisms completely bypassed through address overlap exploitation.

Example Code

Vulnerable Code

// Vulnerable: Address range controller without overlap detection

module vulnerable_address_range_controller (
    input  wire        clk,
    input  wire        rst_n,

    // Range configuration
    input  wire [31:0] protected_base,
    input  wire [31:0] protected_size,
    input  wire [31:0] unprotected_base,
    input  wire [31:0] unprotected_size,
    input  wire        range_write_en,

    // Access request
    input  wire [31:0] access_addr,
    input  wire        access_request,
    input  wire [1:0]  requester_privilege,

    // Access decision
    output reg         access_granted,
    output reg         access_denied
);

    // Range registers
    reg [31:0] prot_base_reg;
    reg [31:0] prot_size_reg;
    reg [31:0] unprot_base_reg;
    reg [31:0] unprot_size_reg;

    // VULNERABLE: No overlap checking when ranges are configured
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            prot_base_reg <= 32'h0000_0000;
            prot_size_reg <= 32'h0000_0000;
            unprot_base_reg <= 32'h0000_0000;
            unprot_size_reg <= 32'h0000_0000;
        end else if (range_write_en) begin
            // VULNERABLE: Accepts any configuration
            prot_base_reg <= protected_base;
            prot_size_reg <= protected_size;
            unprot_base_reg <= unprotected_base;
            unprot_size_reg <= unprotected_size;
        end
    end

    // Calculate range boundaries
    wire [31:0] prot_end = prot_base_reg + prot_size_reg - 1;
    wire [31:0] unprot_end = unprot_base_reg + unprot_size_reg - 1;

    // Check which range the access falls into
    wire in_protected = (access_addr >= prot_base_reg) &&
                        (access_addr <= prot_end);
    wire in_unprotected = (access_addr >= unprot_base_reg) &&
                          (access_addr <= unprot_end);

    // VULNERABLE: When ranges overlap, unprotected takes precedence
    always @(posedge clk) begin
        if (access_request) begin
            if (in_unprotected) begin
                // VULNERABLE: Unprotected access always granted
                // Even if address is ALSO in protected range!
                access_granted <= 1'b1;
                access_denied <= 1'b0;
            end else if (in_protected) begin
                // Protected access requires privilege
                if (requester_privilege >= 2'b10) begin
                    access_granted <= 1'b1;
                    access_denied <= 1'b0;
                end else begin
                    access_granted <= 1'b0;
                    access_denied <= 1'b1;
                end
            end else begin
                access_granted <= 1'b1;
                access_denied <= 1'b0;
            end
        end
    end

    // Attack:
    // Protected range: 0x1000-0x1FFF (contains secrets)
    // Attacker configures unprotected range: 0x1800-0x2FFF
    // Overlap at 0x1800-0x1FFF now accessible without privilege!

endmodule
// Vulnerable: Software memory protection without overlap validation

#include <stdint.h>
#include <stdbool.h>

typedef struct {
    uint32_t base;
    uint32_t size;
    uint8_t  protection_level;
} memory_range_t;

#define MAX_RANGES 16

// VULNERABLE: Global ranges without overlap checking
static memory_range_t ranges[MAX_RANGES];
static int num_ranges = 0;

// VULNERABLE: Adds range without checking for overlaps
bool vulnerable_add_range(uint32_t base, uint32_t size, uint8_t protection) {
    if (num_ranges >= MAX_RANGES) {
        return false;
    }

    // VULNERABLE: No overlap detection
    ranges[num_ranges].base = base;
    ranges[num_ranges].size = size;
    ranges[num_ranges].protection_level = protection;
    num_ranges++;

    return true;
}

// VULNERABLE: Access check with conflicting ranges
bool vulnerable_check_access(uint32_t addr, uint8_t requester_level) {
    uint8_t required_level = 0;
    bool found_unprotected = false;

    for (int i = 0; i < num_ranges; i++) {
        uint32_t range_end = ranges[i].base + ranges[i].size - 1;

        if (addr >= ranges[i].base && addr <= range_end) {
            if (ranges[i].protection_level == 0) {
                // VULNERABLE: Unprotected range found - allow access
                found_unprotected = true;
            } else {
                required_level = ranges[i].protection_level;
            }
        }
    }

    // VULNERABLE: If ANY overlapping range is unprotected, access granted
    if (found_unprotected) {
        return true;  // Bypass protected range!
    }

    return requester_level >= required_level;
}

// Attack:
// 1. Protected range added: 0x1000-0x2000, level 3
// 2. Attacker adds unprotected range: 0x1500-0x1800, level 0
// 3. Access to 0x1500-0x1800 now bypasses protection

Fixed Code

// Fixed: Address range controller with overlap detection and prevention

module secure_address_range_controller (
    input  wire        clk,
    input  wire        rst_n,

    // Range configuration
    input  wire [31:0] protected_base,
    input  wire [31:0] protected_size,
    input  wire [31:0] unprotected_base,
    input  wire [31:0] unprotected_size,
    input  wire        range_write_en,
    input  wire        privileged_config,

    // Access request
    input  wire [31:0] access_addr,
    input  wire        access_request,
    input  wire [1:0]  requester_privilege,

    // Access decision
    output reg         access_granted,
    output reg         access_denied,
    output reg         overlap_detected,
    output reg         config_denied
);

    // Range registers
    reg [31:0] prot_base_reg;
    reg [31:0] prot_size_reg;
    reg [31:0] unprot_base_reg;
    reg [31:0] unprot_size_reg;

    // FIXED: Calculate range boundaries for overlap check
    wire [31:0] new_prot_end = protected_base + protected_size - 1;
    wire [31:0] new_unprot_end = unprotected_base + unprotected_size - 1;

    // FIXED: Overlap detection logic
    wire ranges_overlap = !((new_prot_end < unprotected_base) ||
                            (new_unprot_end < protected_base));

    // FIXED: Validate and reject overlapping configurations
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            prot_base_reg <= 32'h0000_0000;
            prot_size_reg <= 32'h0000_0000;
            unprot_base_reg <= 32'h0000_0000;
            unprot_size_reg <= 32'h0000_0000;
            overlap_detected <= 1'b0;
            config_denied <= 1'b0;
        end else if (range_write_en) begin
            overlap_detected <= 1'b0;
            config_denied <= 1'b0;

            // FIXED: Check for privilege
            if (!privileged_config) begin
                config_denied <= 1'b1;
            end
            // FIXED: Reject overlapping configurations
            else if (ranges_overlap && protected_size > 0 && unprotected_size > 0) begin
                overlap_detected <= 1'b1;
                config_denied <= 1'b1;
                // Do not update registers
            end else begin
                // FIXED: Only update if no overlap
                prot_base_reg <= protected_base;
                prot_size_reg <= protected_size;
                unprot_base_reg <= unprotected_base;
                unprot_size_reg <= unprotected_size;
            end
        end
    end

    // Calculate current range boundaries
    wire [31:0] prot_end = prot_base_reg + prot_size_reg - 1;
    wire [31:0] unprot_end = unprot_base_reg + unprot_size_reg - 1;

    // Check which range the access falls into
    wire in_protected = (prot_size_reg > 0) &&
                        (access_addr >= prot_base_reg) &&
                        (access_addr <= prot_end);
    wire in_unprotected = (unprot_size_reg > 0) &&
                          (access_addr >= unprot_base_reg) &&
                          (access_addr <= unprot_end);

    // FIXED: Protected range takes precedence (defense in depth)
    always @(posedge clk) begin
        if (access_request) begin
            if (in_protected) begin
                // FIXED: Protected check ALWAYS applies
                if (requester_privilege >= 2'b10) begin
                    access_granted <= 1'b1;
                    access_denied <= 1'b0;
                end else begin
                    access_granted <= 1'b0;
                    access_denied <= 1'b1;
                end
            end else if (in_unprotected) begin
                access_granted <= 1'b1;
                access_denied <= 1'b0;
            end else begin
                // Default policy for unmapped regions
                access_granted <= 1'b0;
                access_denied <= 1'b1;
            end
        end
    end

endmodule
// Fixed: Software memory protection with overlap validation

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

typedef struct {
    uint32_t base;
    uint32_t size;
    uint8_t  protection_level;
} memory_range_t;

#define MAX_RANGES 16

static memory_range_t ranges[MAX_RANGES];
static int num_ranges = 0;

// FIXED: Check if two ranges overlap
static bool ranges_overlap(uint32_t base1, uint32_t size1,
                           uint32_t base2, uint32_t size2) {
    if (size1 == 0 || size2 == 0) {
        return false;
    }

    uint32_t end1 = base1 + size1 - 1;
    uint32_t end2 = base2 + size2 - 1;

    return !(end1 < base2 || end2 < base1);
}

// FIXED: Add range with overlap checking
bool secure_add_range(uint32_t base, uint32_t size, uint8_t protection) {
    if (num_ranges >= MAX_RANGES) {
        return false;
    }

    if (size == 0) {
        return false;
    }

    // FIXED: Check for overlaps with existing ranges
    for (int i = 0; i < num_ranges; i++) {
        if (ranges_overlap(base, size, ranges[i].base, ranges[i].size)) {
            // FIXED: Reject if protection levels differ
            if (ranges[i].protection_level != protection) {
                return false;  // Overlap with different protection denied
            }
        }
    }

    ranges[num_ranges].base = base;
    ranges[num_ranges].size = size;
    ranges[num_ranges].protection_level = protection;
    num_ranges++;

    return true;
}

// FIXED: Access check with most restrictive policy
bool secure_check_access(uint32_t addr, uint8_t requester_level) {
    uint8_t max_required_level = 0;
    bool in_any_range = false;

    for (int i = 0; i < num_ranges; i++) {
        uint32_t range_end = ranges[i].base + ranges[i].size - 1;

        if (addr >= ranges[i].base && addr <= range_end) {
            in_any_range = true;
            // FIXED: Use MOST restrictive protection level
            if (ranges[i].protection_level > max_required_level) {
                max_required_level = ranges[i].protection_level;
            }
        }
    }

    // FIXED: Deny access to addresses not in any defined range
    if (!in_any_range) {
        return false;
    }

    return requester_level >= max_required_level;
}

// FIXED: Validation function
bool validate_no_security_overlaps(void) {
    for (int i = 0; i < num_ranges; i++) {
        for (int j = i + 1; j < num_ranges; j++) {
            if (ranges_overlap(ranges[i].base, ranges[i].size,
                              ranges[j].base, ranges[j].size)) {
                // Check if protection levels are consistent
                if (ranges[i].protection_level != ranges[j].protection_level) {
                    return false;  // Security violation!
                }
            }
        }
    }
    return true;
}

CVE Examples

  • CVE-2009-4419: Attackers modified the MCHBAR register to create overlaps, preventing the SENTER instruction from properly applying VT-d protection during Measured Launch Environment initialization.
  • CVE-2019-0151: Memory range overlaps allowed bypassing Intel TXT protections.

  • CWE-284: Improper Access Control (parent)
  • CWE-1203: Peripherals, On-chip Fabric, and Interface/IO Problems (category)
  • CWE-1260: Improper Handling of Overlap Between Protected Memory Ranges (related)
  • CWE-1312: Missing Protection for Mirrored Regions in On-Chip Fabric Firewall (related)

References

  1. MITRE Corporation. "CWE-1316: Fabric-Address Map Allows Programming of Unwarranted Overlaps of Protected and Unprotected Ranges." https://cwe.mitre.org/data/definitions/1316.html
  2. Intel. "Memory-Mapped I/O Configuration Guidelines"
  3. CAPEC-456: Infected Memory
  4. CAPEC-679: Exploitation of Improperly Configured Memory Protections