Hardware Logic Contains Race Conditions
Description
Hardware Logic Contains Race Conditions occurs when a race condition in the hardware logic results in undermining security guarantees of the system. Race conditions in logic circuits occur when logic gates receive inputs from signals that originate from the same source but traverse different paths, causing inputs to change at slightly different times. This creates timing errors or glitches that cause outputs to shift to unwanted states before stabilizing. When these errors occur in access control logic or security-critical finite state machines, attackers may exploit them to bypass existing protections.
Risk
Hardware race conditions have severe security implications. Security protections bypassed during glitches. Privilege escalation possible. Execution logic alterable. Access control decisions corrupted. State machines enter unexpected states. Metastability issues in security logic. Attackers can time operations to exploit races. Physical attacks can induce race conditions.
Solution
Adopt design practices that recognize and eliminate race conditions, such as Karnaugh maps, during architecture and design phase. Implement logic redundancy along security-critical paths. Default to a secure state where access is denied to untrusted agents to avoid metastability issues. Add consensus logic to eliminate glitches. Register inputs to security-critical logic to prevent mid-operation changes.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Protection mechanisms bypassed during race condition windows. |
| Authorization | Scope: Authorization Privilege escalation or identity assumption through timing attacks. |
| Integrity | Scope: Integrity Execution logic altered during glitches. |
Example Code
Vulnerable Code
// Vulnerable: Multiplexor with glitch (race condition)
module vulnerable_glitch_mux (
input wire in0,
input wire in1,
input wire sel,
output wire z
);
wire not_sel, and_out1, and_out2;
// VULNERABLE: Creates glitch in signal z
// When sel changes, there's a brief window where both
// and_out1 and and_out2 could be high or low incorrectly
assign not_sel = ~sel;
assign and_out1 = not_sel & in0;
assign and_out2 = sel & in1;
assign z = and_out1 | and_out2; // VULNERABLE: Glitch occurs here
// Race condition:
// When sel transitions 0->1:
// 1. sel becomes 1
// 2. not_sel still 1 (propagation delay)
// 3. Brief moment: and_out1=in0, and_out2=in1 (both could be 1!)
// 4. z glitches to unexpected value
// 5. Eventually settles to correct value
// If z controls access decisions, brief glitch could allow access
endmodule
// Vulnerable: Security decision with race condition
module vulnerable_access_control (
input wire clk,
input wire rst_n,
input wire user_request,
input wire admin_request,
input wire is_admin,
output wire access_granted
);
wire user_access, admin_access;
// VULNERABLE: Race between is_admin and request signals
assign user_access = user_request & ~is_admin;
assign admin_access = admin_request & is_admin;
assign access_granted = user_access | admin_access;
// Attack: Toggle is_admin while request is active
// Glitch could grant admin access to non-admin user
endmodule
// Vulnerable: DMA with race condition on PMP signals
module vulnerable_dma_pmp (
input wire clk_i,
input wire rst_ni,
input wire [7:0][15:0] pmpcfg_i, // PMP configuration
input wire [15:0][53:0] pmpaddr_i, // PMP addresses
input wire [2:0] dma_ctrl_reg,
input wire [31:0] dma_src_addr,
input wire [31:0] dma_dst_addr,
output reg dma_transfer_valid
);
localparam CTRL_IDLE = 3'b000;
localparam CTRL_SETUP = 3'b001;
localparam CTRL_TRANSFER = 3'b010;
localparam CTRL_DONE = 3'b011;
wire pmp_allow;
// VULNERABLE: Direct use of asynchronous PMP signals
// pmpaddr_i and pmpcfg_i can change during DMA operation
pmp_checker u_pmp (
.cfg(pmpcfg_i), // VULNERABLE: Direct connection
.addr(pmpaddr_i), // VULNERABLE: Can change mid-transfer
.check_addr(dma_src_addr),
.allow(pmp_allow)
);
always @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
dma_transfer_valid <= 1'b0;
end else begin
case (dma_ctrl_reg)
CTRL_SETUP: begin
// VULNERABLE: PMP checked at setup
// But signals can change during transfer
if (pmp_allow) begin
dma_transfer_valid <= 1'b1;
end
end
CTRL_TRANSFER: begin
// VULNERABLE: No re-check during transfer
// Attacker can swap PMP config after initial check
// to access privileged memory
end
CTRL_DONE: begin
dma_transfer_valid <= 1'b0;
end
endcase
end
end
// Attack:
// 1. Set PMP to allow access to unprivileged region
// 2. Start DMA transfer (passes check)
// 3. During transfer, modify PMP to point to privileged region
// 4. DMA continues with initial "allow" status to privileged memory!
endmodule
// Vulnerable: State machine with race condition
module vulnerable_security_fsm (
input wire clk,
input wire rst_n,
input wire auth_signal,
input wire access_request,
output reg secure_access
);
// VULNERABLE: Combinational security decision
// Subject to race conditions on inputs
localparam STATE_LOCKED = 2'b00;
localparam STATE_AUTH = 2'b01;
localparam STATE_OPEN = 2'b10;
reg [1:0] state;
// VULNERABLE: Direct combinational logic for access
always @(*) begin
// Race condition when auth_signal and access_request change
secure_access = (state == STATE_OPEN) ||
(auth_signal && access_request); // GLITCH!
end
endmodule
Fixed Code
// Fixed: Glitch-free multiplexor with consensus logic
module secure_glitch_free_mux (
input wire in0,
input wire in1,
input wire sel,
output wire z
);
wire not_sel, and_out1, and_out2;
assign not_sel = ~sel;
assign and_out1 = not_sel & in0;
assign and_out2 = sel & in1;
// FIXED: Add consensus term to eliminate glitch
// When in0 == in1, output is correct regardless of sel glitch
assign z = and_out1 | and_out2 | (in0 & in1);
// Alternative: Register the output
// reg z_reg;
// always @(posedge clk) z_reg <= and_out1 | and_out2;
endmodule
// Fixed: Security decision with registered inputs
module secure_access_control (
input wire clk,
input wire rst_n,
input wire user_request,
input wire admin_request,
input wire is_admin,
output reg access_granted
);
// FIXED: Register all inputs to synchronize
reg user_request_r, admin_request_r, is_admin_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
user_request_r <= 1'b0;
admin_request_r <= 1'b0;
is_admin_r <= 1'b0;
end else begin
// FIXED: Sample inputs synchronously
user_request_r <= user_request;
admin_request_r <= admin_request;
is_admin_r <= is_admin;
end
end
// FIXED: Make security decision with registered values
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
access_granted <= 1'b0;
end else begin
// FIXED: Default deny, explicit grant
access_granted <= 1'b0;
if (is_admin_r && admin_request_r) begin
access_granted <= 1'b1;
end else if (!is_admin_r && user_request_r) begin
access_granted <= 1'b1;
end
end
end
endmodule
// Fixed: DMA with registered PMP signals
module secure_dma_pmp (
input wire clk_i,
input wire rst_ni,
input wire [7:0][15:0] pmpcfg_i,
input wire [15:0][53:0] pmpaddr_i,
input wire [2:0] dma_ctrl_reg,
input wire [31:0] dma_src_addr,
input wire [31:0] dma_dst_addr,
output reg dma_transfer_valid
);
localparam CTRL_IDLE = 3'b000;
localparam CTRL_SETUP = 3'b001;
localparam CTRL_TRANSFER = 3'b010;
localparam CTRL_DONE = 3'b011;
// FIXED: Register PMP signals at start of operation
reg [7:0][15:0] pmpcfg_reg;
reg [15:0][53:0] pmpaddr_reg;
wire pmp_allow;
// FIXED: Capture PMP configuration at idle/done states only
always @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
pmpcfg_reg <= '0;
pmpaddr_reg <= '0;
end else begin
// FIXED: Only update when not in active transfer
if (dma_ctrl_reg == CTRL_IDLE || dma_ctrl_reg == CTRL_DONE) begin
pmpcfg_reg <= pmpcfg_i;
pmpaddr_reg <= pmpaddr_i;
end
// FIXED: PMP values locked during transfer
end
end
// FIXED: Use registered values for security check
pmp_checker u_pmp (
.cfg(pmpcfg_reg), // FIXED: Registered value
.addr(pmpaddr_reg), // FIXED: Cannot change mid-transfer
.check_addr(dma_src_addr),
.allow(pmp_allow)
);
always @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
dma_transfer_valid <= 1'b0;
end else begin
case (dma_ctrl_reg)
CTRL_SETUP: begin
// FIXED: Check uses stable registered values
if (pmp_allow) begin
dma_transfer_valid <= 1'b1;
end else begin
dma_transfer_valid <= 1'b0;
end
end
CTRL_TRANSFER: begin
// FIXED: Transfer uses same values as check
// Attacker cannot change PMP during transfer
end
CTRL_DONE, CTRL_IDLE: begin
dma_transfer_valid <= 1'b0;
end
default: begin
dma_transfer_valid <= 1'b0;
end
endcase
end
end
endmodule
// Fixed: Security FSM with proper synchronization
module secure_security_fsm (
input wire clk,
input wire rst_n,
input wire auth_signal,
input wire access_request,
output reg secure_access
);
localparam STATE_LOCKED = 2'b00;
localparam STATE_AUTH = 2'b01;
localparam STATE_OPEN = 2'b10;
reg [1:0] state;
// FIXED: Register inputs
reg auth_signal_r, access_request_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
auth_signal_r <= 1'b0;
access_request_r <= 1'b0;
end else begin
auth_signal_r <= auth_signal;
access_request_r <= access_request;
end
end
// FIXED: State machine with registered security decision
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= STATE_LOCKED;
secure_access <= 1'b0; // FIXED: Default deny
end else begin
// FIXED: Default deny each cycle
secure_access <= 1'b0;
case (state)
STATE_LOCKED: begin
if (auth_signal_r) begin
state <= STATE_AUTH;
end
end
STATE_AUTH: begin
if (auth_signal_r && access_request_r) begin
state <= STATE_OPEN;
end else if (!auth_signal_r) begin
state <= STATE_LOCKED;
end
end
STATE_OPEN: begin
// FIXED: Grant access only in proper state
secure_access <= 1'b1;
if (!auth_signal_r) begin
state <= STATE_LOCKED;
secure_access <= 1'b0;
end
end
default: begin
state <= STATE_LOCKED;
end
endcase
end
end
endmodule
CVE Examples
- CVE-2020-15802: Race condition in Bluetooth pairing allowed attackers to inject keys.
- CVE-2019-11091: Hardware race condition enabled microarchitectural data sampling.
Related CWEs
- CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (parent)
- CWE-1199: General Circuit and Logic Design Concerns (category)
- CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition (related)
References
- MITRE Corporation. "CWE-1298: Hardware Logic Contains Race Conditions." https://cwe.mitre.org/data/definitions/1298.html
- CAPEC-26: Leveraging Race Conditions
- IEEE. "Digital Design Principles"