Improper Prevention of Lock Bit Modification
Description
Improper Prevention of Lock Bit Modification occurs when a system employs a trusted lock bit to restrict access to registers or resources, but fails to prevent modification of the lock bit itself after initialization. Hardware devices typically lock configuration controls after power-on using a lock bit mechanism that disables further writes to protected registers. However, design or implementation flaws may allow attackers to modify or clear the lock bit post-initialization, potentially unlocking protected features and compromising system security.
Risk
Lock bit modification vulnerabilities have severe security implications. Protected configurations can be unlocked and modified. Security settings may be disabled after boot. Debug interfaces may be re-enabled. Secure boot protections may be bypassed. Fuse-based protections may be circumvented. Firmware integrity may be compromised. Privilege escalation may be possible. Hardware security boundaries may be violated.
Solution
Protect lock bits from modification once set. Ensure lock bits cannot be cleared by software after initialization. Use hardware-enforced write-once lock mechanisms. Verify all reset paths preserve lock bit state. Remove peripheral reset signals from lock bit reset conditions. Test lock bit persistence across all system states. Implement redundant lock mechanisms. Audit lock bit implementation for bypass paths. Consider using irreversible fuses for critical locks.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Modify Memory - Protected registers can be altered even when the lock is supposedly active, compromising access control protections. |
| Integrity | Scope: Integrity Bypass Protection Mechanism - Security configurations may be unlocked and modified after supposedly being secured. |
Example Code
Vulnerable Code
// Vulnerable: Lock bit can be cleared through peripheral reset
module vulnerable_register_lock (
input wire clk,
input wire rst_ni, // Global reset
input wire jtag_unlock, // JTAG unlock signal
input wire rst_peripheral, // Peripheral-specific reset
input wire [31:0] write_data,
input wire write_enable,
input wire lock_write,
output reg [31:0] protected_register,
output reg register_locked
);
always @(posedge clk or negedge rst_ni) begin
// VULNERABLE: Lock reset includes peripheral reset
// Attacker can trigger peripheral reset to clear lock!
if (~(rst_ni && ~jtag_unlock && ~rst_peripheral)) begin
register_locked <= 1'b0; // Lock cleared!
protected_register <= 32'h0;
end
else begin
if (lock_write && !register_locked) begin
register_locked <= 1'b1;
end
if (write_enable && !register_locked) begin
protected_register <= write_data;
end
end
end
endmodule
// Vulnerable: Lock bit stored in regular register (can be written)
module vulnerable_soft_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire [7:0] register_select,
output reg [31:0] config_register,
output reg [31:0] lock_register // VULNERABLE: Lock is writable!
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
config_register <= 32'h0;
lock_register <= 32'h0;
end
else if (write_enable) begin
case (register_select)
8'h00: begin
// Config register - check lock
if (lock_register[0] == 1'b0) begin
config_register <= write_data;
end
end
8'h01: begin
// VULNERABLE: Lock register is writable!
// Attacker can write 0 to clear lock
lock_register <= write_data;
end
endcase
end
end
endmodule
// Vulnerable: Incomplete lock coverage
module vulnerable_incomplete_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
output reg [31:0] temp_register, // Locked
output reg [31:0] calibration_reg, // Locked
output reg [31:0] shutdown_response, // VULNERABLE: Not locked!
output reg register_locked
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
temp_register <= 32'h0;
calibration_reg <= 32'h0;
shutdown_response <= 32'h0;
register_locked <= 1'b0;
end
else begin
// Lock protects temp and calibration
if (!register_locked) begin
temp_register <= write_data;
calibration_reg <= write_data;
end
// VULNERABLE: shutdown_response not protected by lock!
// Attacker can modify safety-critical response
shutdown_response <= write_data;
end
end
endmodule
// Vulnerable: Firmware with clearable lock bit
#define CONFIG_REG_ADDR 0x40001000
#define LOCK_REG_ADDR 0x40001004
void vulnerable_lock_config(void) {
// Configure the register
*(volatile uint32_t*)CONFIG_REG_ADDR = SECURE_CONFIG_VALUE;
// Set lock bit
*(volatile uint32_t*)LOCK_REG_ADDR = 1;
// VULNERABLE: Lock can be cleared!
}
void attacker_clear_lock(void) {
// Hardware doesn't prevent clearing the lock
*(volatile uint32_t*)LOCK_REG_ADDR = 0;
// Now config register is writable again
*(volatile uint32_t*)CONFIG_REG_ADDR = MALICIOUS_CONFIG;
}
// Vulnerable: Using peripheral reset to clear lock
void attacker_reset_to_clear_lock(void) {
// Trigger peripheral reset
trigger_peripheral_reset(PERIPHERAL_ID);
// Lock bit is cleared by reset
// Now can modify supposedly locked registers
}
Fixed Code
// Fixed: Lock bit protected from modification
module secure_register_lock (
input wire clk,
input wire rst_ni, // Global reset only
input wire jtag_unlock, // JTAG unlock (with proper auth)
input wire rst_peripheral, // Peripheral reset - NOT used for lock!
input wire [31:0] write_data,
input wire write_enable,
input wire lock_write,
output reg [31:0] protected_register,
output reg register_locked
);
always @(posedge clk or negedge rst_ni) begin
// FIXED: Lock only cleared by global reset or authenticated JTAG
// Peripheral reset does NOT affect lock
if (~rst_ni) begin
register_locked <= 1'b0;
protected_register <= 32'h0;
end
else if (jtag_unlock) begin
// Only authenticated JTAG can clear lock
register_locked <= 1'b0;
end
else begin
// Lock can only transition from 0 to 1
if (lock_write && !register_locked) begin
register_locked <= 1'b1;
end
// Lock cannot be cleared by software
// Protected register only writable when unlocked
if (write_enable && !register_locked) begin
protected_register <= write_data;
end
end
end
// Peripheral reset only affects data, not lock
always @(posedge clk) begin
if (rst_peripheral && register_locked) begin
// Peripheral reset with lock active: data preserved
// Lock remains set
end
end
endmodule
// Fixed: Write-once lock bit implementation
module secure_write_once_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire [7:0] register_select,
output reg [31:0] config_register,
output reg lock_set // Write-once lock
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
config_register <= 32'h0;
lock_set <= 1'b0;
end
else if (write_enable) begin
case (register_select)
8'h00: begin
// Config register - check lock
if (!lock_set) begin
config_register <= write_data;
end
end
8'h01: begin
// FIXED: Lock can only be SET, never cleared
if (!lock_set && write_data[0]) begin
lock_set <= 1'b1;
end
// Writes to clear lock are ignored
end
endcase
end
end
endmodule
// Fixed: Complete lock coverage for all sensitive registers
module secure_complete_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire [7:0] register_select,
input wire set_lock,
output reg [31:0] temp_register,
output reg [31:0] calibration_reg,
output reg [31:0] shutdown_response,
output reg register_locked
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
temp_register <= 32'h0;
calibration_reg <= 32'h0;
shutdown_response <= 32'h0;
register_locked <= 1'b0;
end
else begin
// Lock can only be set, not cleared
if (set_lock && !register_locked) begin
register_locked <= 1'b1;
end
if (write_enable && !register_locked) begin
case (register_select)
8'h00: temp_register <= write_data;
8'h01: calibration_reg <= write_data;
8'h02: shutdown_response <= write_data; // Now protected!
endcase
end
// All writes blocked when locked
end
end
endmodule
// Fixed: Hardware-enforced permanent lock
module secure_permanent_lock #(
parameter LOCK_ON_BOOT = 1 // Lock immediately after boot config
) (
input wire clk,
input wire reset_n,
input wire boot_complete,
input wire [31:0] write_data,
input wire write_enable,
output reg [31:0] protected_register,
output reg permanently_locked
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
protected_register <= 32'h0;
permanently_locked <= 1'b0;
end
else begin
// Auto-lock when boot completes
if (LOCK_ON_BOOT && boot_complete && !permanently_locked) begin
permanently_locked <= 1'b1;
end
// Only writable before permanent lock
if (write_enable && !permanently_locked) begin
protected_register <= write_data;
end
end
end
// Cannot be unlocked by any means except full reset
endmodule
// Fixed: Firmware with proper lock bit handling
#define CONFIG_REG_ADDR 0x40001000
#define LOCK_REG_ADDR 0x40001004
#define LOCK_STATUS_ADDR 0x40001008
void secure_lock_config(void) {
// Configure the register
*(volatile uint32_t*)CONFIG_REG_ADDR = SECURE_CONFIG_VALUE;
// Set write-once lock bit
*(volatile uint32_t*)LOCK_REG_ADDR = 1;
// Verify lock is set
if (!(*(volatile uint32_t*)LOCK_STATUS_ADDR & 0x1)) {
panic("Failed to set config lock!");
}
// Try to clear (should fail)
*(volatile uint32_t*)LOCK_REG_ADDR = 0;
// Verify lock still set
if (!(*(volatile uint32_t*)LOCK_STATUS_ADDR & 0x1)) {
panic("Lock bit was cleared - hardware vulnerability!");
}
// Try to modify config (should fail)
uint32_t original = *(volatile uint32_t*)CONFIG_REG_ADDR;
*(volatile uint32_t*)CONFIG_REG_ADDR = 0xFFFFFFFF;
if (*(volatile uint32_t*)CONFIG_REG_ADDR != original) {
panic("Locked register was modified - hardware vulnerability!");
}
}
// Verification function
bool verify_lock_bit_implementation(void) {
bool passed = true;
// Test 1: Lock bit can be set
*(volatile uint32_t*)LOCK_REG_ADDR = 1;
if (!(*(volatile uint32_t*)LOCK_STATUS_ADDR & 0x1)) {
log_error("Lock bit cannot be set");
passed = false;
}
// Test 2: Lock bit cannot be cleared by writing 0
*(volatile uint32_t*)LOCK_REG_ADDR = 0;
if (!(*(volatile uint32_t*)LOCK_STATUS_ADDR & 0x1)) {
log_error("Lock bit cleared by writing 0");
passed = false;
}
// Test 3: Protected register cannot be modified when locked
uint32_t before = *(volatile uint32_t*)CONFIG_REG_ADDR;
*(volatile uint32_t*)CONFIG_REG_ADDR = ~before;
uint32_t after = *(volatile uint32_t*)CONFIG_REG_ADDR;
if (after != before) {
log_error("Locked register modified");
passed = false;
}
// Test 4: Peripheral reset doesn't clear lock
trigger_peripheral_reset(PERIPHERAL_ID);
if (!(*(volatile uint32_t*)LOCK_STATUS_ADDR & 0x1)) {
log_error("Peripheral reset cleared lock");
passed = false;
}
return passed;
}
CVE Examples
- CVE-2017-6283: Chip reset clears critical RSA function read/write lock permissions
- OpenPiton SoC vulnerabilities from HACK@DAC'21 demonstrated lock bit bypass through peripheral reset
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1199: General Circuit and Logic Design Concerns (category member)
- CWE-1224: Improper Restriction of Write-Once Bit Fields (related)
- CWE-1233: Security-Sensitive Hardware Controls with Missing Lock Bit Protection (related)
References
- MITRE Corporation. "CWE-1231: Improper Prevention of Lock Bit Modification." https://cwe.mitre.org/data/definitions/1231.html
- Hardware Security Lock Bit Design Guidelines
- OpenPiton SoC Security Analysis