Race Condition for Write-Once Attributes
Description
Race Condition for Write-Once Attributes occurs when a hardware design contains write-once registers or other elements that can be modified by both software and hardware simultaneously, creating a race condition. Write-once registers are commonly used for security policies, access control, OTP (One-Time Programmable) memory, and secure configurations that should only be set once during boot. If both software and hardware can race to set these values, an attacker may win the race and set malicious values before legitimate security configurations are established.
Risk
Race conditions in write-once attributes have severe security implications. Security policies may be set by attackers before legitimate software. Access control configurations may be maliciously established. Boot security measures may be bypassed through race exploitation. OTP fuses may be programmed with attacker values. Debug interfaces may be unlocked before being secured. Privilege levels may be set favorably for attackers. Trust anchors may be compromised during initialization.
Solution
Implement deterministic write order for security-critical registers. Use hardware arbitration to ensure trusted sources write first. Add timing guarantees that secure writes complete before untrusted access. Implement hardware locks that are set atomically with values. Use staged initialization with security checkpoints. Separate write permissions for initial and subsequent access. Verify write-once values before allowing system operation. Implement hardware interlocks preventing concurrent access. Design boot sequences that eliminate race windows.
Common Consequences
| Impact | Details |
|---|---|
| Integrity, Authorization | Scope: Integrity, Authorization Bypass Protection Mechanism, Gain Privileges - Attackers can win the race to set security-critical write-once values, bypassing intended protections or gaining elevated privileges. |
| Access Control | Scope: Access Control Modify Memory - Security configurations may be set to attacker-controlled values before legitimate software acts. |
Example Code
Vulnerable Code
// Vulnerable: Write-once register with race condition
module vulnerable_write_once_register (
input wire clk,
input wire reset_n,
// Software interface
input wire [31:0] sw_write_data,
input wire sw_write_enable,
// Hardware interface (e.g., from boot ROM or fuse controller)
input wire [31:0] hw_write_data,
input wire hw_write_enable,
output reg [31:0] register_value,
output reg register_locked
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
register_value <= 32'h0;
register_locked <= 1'b0;
end
else if (!register_locked) begin
// VULNERABLE: Race condition between software and hardware
// Whoever writes first wins!
if (sw_write_enable && hw_write_enable) begin
// Both trying to write - undefined behavior!
// Could take either value depending on timing
register_value <= sw_write_data; // Or hw_write_data?
register_locked <= 1'b1;
end
else if (sw_write_enable) begin
// Software can win the race
register_value <= sw_write_data;
register_locked <= 1'b1;
end
else if (hw_write_enable) begin
// Hardware sets value
register_value <= hw_write_data;
register_locked <= 1'b1;
end
end
end
endmodule
// Vulnerable: Security policy register with race window
module vulnerable_security_policy (
input wire clk,
input wire reset_n,
input wire [31:0] policy_write,
input wire policy_write_en,
input wire boot_complete,
output reg [31:0] security_policy,
output reg policy_locked
);
// VULNERABLE: Policy can be set by anyone before boot_complete
// Race window exists between reset and boot completion
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
security_policy <= 32'h0; // Insecure default!
policy_locked <= 1'b0;
end
else begin
if (!policy_locked) begin
// Anyone can write during the race window
if (policy_write_en) begin
security_policy <= policy_write;
end
end
// Lock happens independently of write
if (boot_complete) begin
policy_locked <= 1'b1;
end
end
end
// Attacker can:
// 1. Write malicious policy
// 2. Race to complete before legitimate boot code
endmodule
// Vulnerable: OTP fuse programming with race condition
module vulnerable_otp_controller (
input wire clk,
input wire [31:0] fuse_data,
input wire fuse_program,
input wire [7:0] fuse_address,
output reg programming_complete
);
reg [31:0] otp_array [0:255];
reg [255:0] fuse_programmed;
always @(posedge clk) begin
if (fuse_program && !fuse_programmed[fuse_address]) begin
// VULNERABLE: No authentication before fuse programming
// No ordering guarantee for security fuses
otp_array[fuse_address] <= fuse_data;
fuse_programmed[fuse_address] <= 1'b1;
programming_complete <= 1'b1;
end
end
endmodule
// Vulnerable: Software with race condition for write-once config
// Security configuration register
#define SECURITY_CONFIG_REG 0x40000000
#define SECURITY_LOCK_REG 0x40000004
volatile uint32_t* security_config = (uint32_t*)SECURITY_CONFIG_REG;
volatile uint32_t* security_lock = (uint32_t*)SECURITY_LOCK_REG;
void vulnerable_set_security_policy(void) {
// Check if already locked
if (*security_lock) {
return; // Already set
}
// VULNERABLE: Race window between check and write
// Another thread/process/hardware could set value here
// Set our policy
*security_config = SECURE_POLICY_VALUE;
// Lock it
*security_lock = 1;
// Problem: Attacker could have won the race
}
// Vulnerable: Non-atomic check-then-act
bool vulnerable_init_write_once(uint32_t* reg, uint32_t* lock, uint32_t value) {
// VULNERABLE: TOCTOU race condition
if (*lock == 0) { // Check
// Race window here!
*reg = value; // Write
*lock = 1; // Lock
return true;
}
return false;
}
Fixed Code
// Fixed: Write-once register with hardware priority and arbitration
module secure_write_once_register (
input wire clk,
input wire reset_n,
// Software interface
input wire [31:0] sw_write_data,
input wire sw_write_enable,
input wire sw_authenticated,
// Hardware interface (trusted boot ROM)
input wire [31:0] hw_write_data,
input wire hw_write_enable,
// Boot phase tracking
input wire secure_boot_phase,
output reg [31:0] register_value,
output reg register_locked,
output reg write_error
);
// State machine for ordered initialization
localparam STATE_HW_PHASE = 2'b00;
localparam STATE_SW_PHASE = 2'b01;
localparam STATE_LOCKED = 2'b10;
reg [1:0] state;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
register_value <= 32'h0;
register_locked <= 1'b0;
write_error <= 1'b0;
state <= STATE_HW_PHASE;
end
else begin
case (state)
STATE_HW_PHASE: begin
// Only hardware can write during this phase
if (hw_write_enable) begin
register_value <= hw_write_data;
register_locked <= 1'b1;
state <= STATE_LOCKED;
end
else if (sw_write_enable) begin
// Software tried to write too early - error
write_error <= 1'b1;
end
// Transition to SW phase only after HW phase completes
if (secure_boot_phase && !hw_write_enable) begin
state <= STATE_SW_PHASE;
end
end
STATE_SW_PHASE: begin
// Software can write if authenticated and not yet locked
if (sw_write_enable && sw_authenticated && !register_locked) begin
register_value <= sw_write_data;
register_locked <= 1'b1;
state <= STATE_LOCKED;
end
else if (sw_write_enable && !sw_authenticated) begin
write_error <= 1'b1;
end
end
STATE_LOCKED: begin
// No further writes allowed
if (sw_write_enable || hw_write_enable) begin
write_error <= 1'b1;
end
end
endcase
end
end
endmodule
// Fixed: Atomic write-and-lock mechanism
module secure_atomic_write_once (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_and_lock, // Single signal for atomic operation
input wire trusted_source,
output reg [31:0] register_value,
output reg locked,
output reg write_rejected
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
register_value <= 32'h0;
locked <= 1'b0;
write_rejected <= 1'b0;
end
else begin
write_rejected <= 1'b0;
if (write_and_lock) begin
if (!locked && trusted_source) begin
// Atomic: set value AND lock in same cycle
register_value <= write_data;
locked <= 1'b1;
end
else begin
// Already locked or untrusted
write_rejected <= 1'b1;
end
end
end
end
endmodule
// Fixed: Security policy with staged initialization
module secure_security_policy (
input wire clk,
input wire reset_n,
input wire [31:0] policy_write,
input wire policy_write_en,
input wire boot_rom_phase,
input wire trusted_firmware_phase,
input wire untrusted_phase,
output reg [31:0] security_policy,
output reg policy_locked
);
// Staged initialization prevents race conditions
reg [1:0] init_stage;
localparam STAGE_BOOT_ROM = 2'b00;
localparam STAGE_FIRMWARE = 2'b01;
localparam STAGE_LOCKED = 2'b10;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// Secure default policy
security_policy <= 32'hFFFFFFFF; // Most restrictive
policy_locked <= 1'b0;
init_stage <= STAGE_BOOT_ROM;
end
else begin
case (init_stage)
STAGE_BOOT_ROM: begin
// Only boot ROM can modify
if (boot_rom_phase && policy_write_en) begin
security_policy <= policy_write;
end
// Transition when boot ROM signals complete
if (trusted_firmware_phase) begin
init_stage <= STAGE_FIRMWARE;
end
end
STAGE_FIRMWARE: begin
// Trusted firmware can refine (but not weaken)
if (trusted_firmware_phase && policy_write_en) begin
// Can only make policy more restrictive
security_policy <= security_policy & policy_write;
end
// Lock before untrusted code runs
if (untrusted_phase) begin
policy_locked <= 1'b1;
init_stage <= STAGE_LOCKED;
end
end
STAGE_LOCKED: begin
// No modifications allowed
policy_locked <= 1'b1;
end
endcase
end
end
endmodule
// Fixed: Software with race-free write-once handling
#define SECURITY_CONFIG_REG 0x40000000
#define ATOMIC_WRITE_LOCK_REG 0x40000004
// Hardware supports atomic write-and-lock
typedef struct {
uint32_t value;
uint32_t write_and_lock; // Writing here atomically sets value and locks
} atomic_write_once_t;
volatile atomic_write_once_t* security_config =
(atomic_write_once_t*)SECURITY_CONFIG_REG;
bool secure_set_security_policy(uint32_t policy) {
// Atomic write-and-lock operation
// Hardware ensures no race condition
security_config->value = policy;
security_config->write_and_lock = 1;
// Verify our write succeeded
if (security_config->value != policy) {
// Someone else won - security violation
panic("Security policy race condition detected!");
return false;
}
return true;
}
// Fixed: Use hardware mutex for write-once registers
bool secure_init_with_mutex(void) {
// Acquire hardware mutex before touching write-once register
if (!acquire_hardware_mutex(SECURITY_MUTEX)) {
return false; // Could not acquire - someone else is writing
}
// Check if already configured
if (is_security_locked()) {
release_hardware_mutex(SECURITY_MUTEX);
return verify_security_config();
}
// Configure security (we have exclusive access)
set_security_policy(SECURE_POLICY_VALUE);
// Lock configuration
lock_security_config();
release_hardware_mutex(SECURITY_MUTEX);
return verify_security_config();
}
// Verification after write
bool verify_security_config(void) {
uint32_t config = read_security_config();
if (config != SECURE_POLICY_VALUE) {
log_security_violation("Security config mismatch: 0x%08X", config);
return false;
}
if (!is_security_locked()) {
log_security_violation("Security config not locked!");
return false;
}
return true;
}
CVE Examples
Race conditions in write-once registers have been exploited to bypass secure boot, unlock debug interfaces, and modify security policies in various hardware platforms.
Related CWEs
- CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (parent)
- CWE-1199: General Circuit and Logic Design Concerns (category member)
- CWE-1221: Incorrect Register Defaults or Module Parameters (related)
References
- MITRE Corporation. "CWE-1223: Race Condition for Write-Once Attributes." https://cwe.mitre.org/data/definitions/1223.html
- Hardware Race Condition Mitigation
- Secure Boot Implementation Guidelines