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
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Protected data readable through unprotected overlapping address range. |
| Integrity | Scope: Integrity Memory modification via the unprotected portion of overlapped regions. |
| Access Control | Scope: 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.
Related CWEs
- 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
- 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
- Intel. "Memory-Mapped I/O Configuration Guidelines"
- CAPEC-456: Infected Memory
- CAPEC-679: Exploitation of Improperly Configured Memory Protections