Improper Access Control for Register Interface
Description
Improper Access Control for Register Interface occurs when a product uses memory-mapped I/O registers providing hardware access from software but lacks proper access controls to those registers. Peripherals in Systems-on-Chip (SoC) and other devices are commonly accessed through memory-mapped register interfaces. Without adequate protections, malicious software could compromise security-critical hardware data, leading to loss of confidentiality and integrity.
Risk
Unprotected register interfaces have severe security implications. Cryptographic keys may be readable. Security configurations may be modified. Hardware state may be corrupted. Protection mechanisms may be bypassed. Privilege escalation becomes possible. Security-critical data may be altered. Unexpected hardware states may be triggered. Execution logic may be modified maliciously.
Solution
Establish proper policies for hardware register access from software. Ensure access control policies are correctly implemented per specifications. Implement privilege-level checks for register access. Disable read-back paths for sensitive data like cryptographic keys. Use hardware-enforced access control. Validate all register addresses and values. Implement proper CSR protection in processors.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Attackers may read protected information through unprotected registers. |
| Integrity | Scope: Integrity Modify Memory - Security state, settings, and critical data may be corrupted or modified. |
| Access Control | Scope: Access Control Gain Privileges - Unauthorized privilege escalation through register manipulation. |
Example Code
Vulnerable Code
// Vulnerable: Cryptographic accelerator with readable keys
module vulnerable_crypto_accelerator (
input wire clk,
input wire reset_n,
input wire [7:0] reg_addr,
input wire [31:0] reg_write_data,
input wire reg_write,
input wire reg_read,
output reg [31:0] reg_read_data
);
// Key storage
reg [255:0] crypto_key;
// Status and control registers
reg [31:0] status_reg;
reg [31:0] control_reg;
// Register addresses
parameter KEY_REG_0 = 8'h00;
parameter KEY_REG_1 = 8'h04;
parameter KEY_REG_2 = 8'h08;
parameter KEY_REG_3 = 8'h0C;
parameter KEY_REG_4 = 8'h10;
parameter KEY_REG_5 = 8'h14;
parameter KEY_REG_6 = 8'h18;
parameter KEY_REG_7 = 8'h1C;
parameter STATUS_REG = 8'h20;
parameter CONTROL_REG = 8'h24;
// Key write
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
crypto_key <= 256'h0;
end
else if (reg_write) begin
case (reg_addr)
KEY_REG_0: crypto_key[31:0] <= reg_write_data;
KEY_REG_1: crypto_key[63:32] <= reg_write_data;
KEY_REG_2: crypto_key[95:64] <= reg_write_data;
KEY_REG_3: crypto_key[127:96] <= reg_write_data;
KEY_REG_4: crypto_key[159:128] <= reg_write_data;
KEY_REG_5: crypto_key[191:160] <= reg_write_data;
KEY_REG_6: crypto_key[223:192] <= reg_write_data;
KEY_REG_7: crypto_key[255:224] <= reg_write_data;
CONTROL_REG: control_reg <= reg_write_data;
endcase
end
end
// VULNERABLE: Key registers are readable!
always @(*) begin
case (reg_addr)
// VULNERABLE: Key can be read back by any software
KEY_REG_0: reg_read_data = crypto_key[31:0];
KEY_REG_1: reg_read_data = crypto_key[63:32];
KEY_REG_2: reg_read_data = crypto_key[95:64];
KEY_REG_3: reg_read_data = crypto_key[127:96];
KEY_REG_4: reg_read_data = crypto_key[159:128];
KEY_REG_5: reg_read_data = crypto_key[191:160];
KEY_REG_6: reg_read_data = crypto_key[223:192];
KEY_REG_7: reg_read_data = crypto_key[255:224];
STATUS_REG: reg_read_data = status_reg;
CONTROL_REG: reg_read_data = control_reg;
default: reg_read_data = 32'h0;
endcase
end
endmodule
// Vulnerable: RISC-V CSR without proper privilege check
module vulnerable_csr_module (
input wire clk,
input wire reset_n,
input wire [11:0] csr_addr,
input wire [31:0] csr_write_data,
input wire csr_write,
input wire csr_read,
input wire [1:0] privilege_level, // 0=U, 1=S, 3=M
output reg [31:0] csr_read_data,
output reg illegal_access
);
// Machine-level CSRs (require M-mode)
reg [31:0] mstatus;
reg [31:0] mepc;
reg [31:0] mcause;
reg [31:0] mtvec;
// CSR addresses
parameter MSTATUS = 12'h300;
parameter MEPC = 12'h341;
parameter MCAUSE = 12'h342;
parameter MTVEC = 12'h305;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
mstatus <= 32'h0;
mepc <= 32'h0;
mcause <= 32'h0;
mtvec <= 32'h0;
illegal_access <= 1'b0;
end
else if (csr_write) begin
// VULNERABLE: MEPC excluded from privilege check!
if (csr_addr == MEPC) begin
// VULNERABLE: Any privilege level can write MEPC
mepc <= csr_write_data;
end
// Other CSRs properly protected
else if (csr_addr[11:10] == 2'b11 && privilege_level != 2'd3) begin
illegal_access <= 1'b1;
end
else begin
case (csr_addr)
MSTATUS: mstatus <= csr_write_data;
MCAUSE: mcause <= csr_write_data;
MTVEC: mtvec <= csr_write_data;
endcase
end
end
end
// Attack: User mode writes MEPC to redirect exception return
// to arbitrary code, achieving privilege escalation
endmodule
// Vulnerable: Software accessing unprotected registers
#include <stdint.h>
#define CRYPTO_BASE 0x40000000
#define CRYPTO_KEY_REG_0 (CRYPTO_BASE + 0x00)
#define CRYPTO_KEY_REG_1 (CRYPTO_BASE + 0x04)
// ...
// VULNERABLE: Any process can read the key
void vulnerable_read_key(uint8_t* key_buffer) {
volatile uint32_t* key_regs = (volatile uint32_t*)CRYPTO_KEY_REG_0;
// VULNERABLE: No access control check
// Malicious process can read cryptographic key
for (int i = 0; i < 8; i++) {
((uint32_t*)key_buffer)[i] = key_regs[i];
}
}
// VULNERABLE: Any process can modify security registers
void vulnerable_modify_security_config(void) {
volatile uint32_t* security_ctrl = (volatile uint32_t*)0x40001000;
// VULNERABLE: No privilege check
*security_ctrl = 0; // Disable security features
}
Fixed Code
// Fixed: Cryptographic accelerator with protected keys
module secure_crypto_accelerator (
input wire clk,
input wire reset_n,
input wire [7:0] reg_addr,
input wire [31:0] reg_write_data,
input wire reg_write,
input wire reg_read,
input wire [1:0] privilege_level, // 0=user, 1=supervisor, 2=hypervisor, 3=secure
input wire secure_access, // Hardware-enforced secure access signal
output reg [31:0] reg_read_data,
output reg access_denied
);
// Key storage
reg [255:0] crypto_key;
reg key_loaded;
// Status and control registers
reg [31:0] status_reg;
reg [31:0] control_reg;
// Register addresses
parameter KEY_REG_0 = 8'h00;
parameter KEY_REG_1 = 8'h04;
parameter KEY_REG_2 = 8'h08;
parameter KEY_REG_3 = 8'h0C;
parameter KEY_REG_4 = 8'h10;
parameter KEY_REG_5 = 8'h14;
parameter KEY_REG_6 = 8'h18;
parameter KEY_REG_7 = 8'h1C;
parameter STATUS_REG = 8'h20;
parameter CONTROL_REG = 8'h24;
// FIXED: Key addresses are write-only, require secure access
wire is_key_addr = (reg_addr >= KEY_REG_0) && (reg_addr <= KEY_REG_7);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
crypto_key <= 256'h0;
key_loaded <= 1'b0;
access_denied <= 1'b0;
end
else begin
access_denied <= 1'b0;
if (reg_write) begin
// FIXED: Key writes require secure access
if (is_key_addr) begin
if (!secure_access) begin
access_denied <= 1'b1;
end
else begin
case (reg_addr)
KEY_REG_0: crypto_key[31:0] <= reg_write_data;
KEY_REG_1: crypto_key[63:32] <= reg_write_data;
KEY_REG_2: crypto_key[95:64] <= reg_write_data;
KEY_REG_3: crypto_key[127:96] <= reg_write_data;
KEY_REG_4: crypto_key[159:128] <= reg_write_data;
KEY_REG_5: crypto_key[191:160] <= reg_write_data;
KEY_REG_6: crypto_key[223:192] <= reg_write_data;
KEY_REG_7: begin
crypto_key[255:224] <= reg_write_data;
key_loaded <= 1'b1;
end
endcase
end
end
// Control register requires at least supervisor
else if (reg_addr == CONTROL_REG) begin
if (privilege_level < 2'd1) begin
access_denied <= 1'b1;
end
else begin
control_reg <= reg_write_data;
end
end
end
end
end
// FIXED: Key registers are NOT readable
always @(*) begin
access_denied = 1'b0;
if (reg_read) begin
case (reg_addr)
// FIXED: Key registers return zero, not actual key
KEY_REG_0, KEY_REG_1, KEY_REG_2, KEY_REG_3,
KEY_REG_4, KEY_REG_5, KEY_REG_6, KEY_REG_7: begin
reg_read_data = 32'h0; // Write-only
access_denied = 1'b1; // Flag access attempt
end
STATUS_REG: reg_read_data = status_reg;
CONTROL_REG: begin
if (privilege_level < 2'd1) begin
reg_read_data = 32'h0;
access_denied = 1'b1;
end
else begin
reg_read_data = control_reg;
end
end
default: reg_read_data = 32'h0;
endcase
end
else begin
reg_read_data = 32'h0;
end
end
endmodule
// Fixed: RISC-V CSR with proper privilege check
module secure_csr_module (
input wire clk,
input wire reset_n,
input wire [11:0] csr_addr,
input wire [31:0] csr_write_data,
input wire csr_write,
input wire csr_read,
input wire [1:0] privilege_level,
output reg [31:0] csr_read_data,
output reg illegal_access
);
// Machine-level CSRs
reg [31:0] mstatus;
reg [31:0] mepc;
reg [31:0] mcause;
reg [31:0] mtvec;
parameter MSTATUS = 12'h300;
parameter MEPC = 12'h341;
parameter MCAUSE = 12'h342;
parameter MTVEC = 12'h305;
// FIXED: Extract required privilege from CSR address
wire [1:0] required_privilege = csr_addr[9:8];
// FIXED: All M-mode CSRs require M-mode privilege
wire privilege_sufficient = (privilege_level >= required_privilege);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
mstatus <= 32'h0;
mepc <= 32'h0;
mcause <= 32'h0;
mtvec <= 32'h0;
illegal_access <= 1'b0;
end
else begin
illegal_access <= 1'b0;
if (csr_write) begin
// FIXED: Check privilege for ALL CSRs including MEPC
if (!privilege_sufficient) begin
illegal_access <= 1'b1;
end
else begin
case (csr_addr)
MSTATUS: mstatus <= csr_write_data;
MEPC: mepc <= csr_write_data; // FIXED: Now protected
MCAUSE: mcause <= csr_write_data;
MTVEC: mtvec <= csr_write_data;
default: illegal_access <= 1'b1;
endcase
end
end
if (csr_read) begin
if (!privilege_sufficient) begin
illegal_access <= 1'b1;
csr_read_data <= 32'h0;
end
else begin
case (csr_addr)
MSTATUS: csr_read_data <= mstatus;
MEPC: csr_read_data <= mepc;
MCAUSE: csr_read_data <= mcause;
MTVEC: csr_read_data <= mtvec;
default: csr_read_data <= 32'h0;
endcase
end
end
end
end
endmodule
// Fixed: Software with proper register access control
#include <stdint.h>
#define CRYPTO_BASE 0x40000000
// FIXED: Key loading only through secure API
int secure_load_key(const uint8_t* key, size_t key_len) {
// FIXED: Check caller privilege
if (get_current_privilege() < PRIVILEGE_SECURE) {
log_security_event("Unprivileged key load attempt");
return -EPERM;
}
// FIXED: Validate key length
if (key_len != 32) {
return -EINVAL;
}
// FIXED: Use secure hardware path
volatile uint32_t* key_regs = (volatile uint32_t*)(CRYPTO_BASE + 0x00);
// Disable interrupts during key loading
uint32_t irq_state = disable_interrupts();
for (int i = 0; i < 8; i++) {
key_regs[i] = ((uint32_t*)key)[i];
}
// Memory barrier
__sync_synchronize();
restore_interrupts(irq_state);
return 0;
}
// FIXED: Key read returns error
int secure_read_key(uint8_t* key_buffer) {
// FIXED: Keys are never readable
log_security_event("Attempt to read cryptographic key");
return -EPERM;
}
// FIXED: Security configuration requires privilege
int secure_modify_security_config(uint32_t config) {
if (get_current_privilege() < PRIVILEGE_KERNEL) {
return -EPERM;
}
// Validate configuration value
if (!is_valid_security_config(config)) {
return -EINVAL;
}
volatile uint32_t* security_ctrl = (volatile uint32_t*)0x40001000;
*security_ctrl = config;
return 0;
}
CVE Examples
- RISC-V processor implementations where MEPC CSR was incorrectly excluded from privilege checks, allowing user-mode privilege escalation
- Various SoC vulnerabilities where cryptographic key registers were readable by unprivileged software
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1220: Insufficient Granularity of Access Control (related)
- CAPEC-680: Exploitation of Improperly Controlled Registers (attack pattern)
References
- MITRE Corporation. "CWE-1262: Improper Access Control for Register Interface." https://cwe.mitre.org/data/definitions/1262.html
- RISC-V. "Privileged Architecture Specification"
- ARM. "TrustZone Security Technology"