Hardware Internal or Debug Modes Allow Override of Locks
Description
Hardware Internal or Debug Modes Allow Override of Locks occurs when system configuration protection can be bypassed during debug mode or other internal hardware modes. Device configuration controls are typically locked after a trusted firmware module (e.g., BIOS/bootloader) sets them during power reset. This lock bit prevents further modification of system configuration like memory protection settings. However, if the hardware supports debug features or internal modes, these can potentially allow bypassing the lock protection and modifying supposedly protected configuration.
Risk
Debug mode lock override has severe security implications. Security locks may be bypassed through debug interfaces. Protected configuration may be modified in test mode. JTAG access may clear lock bits. Scan mode may allow lock bypass. Manufacturing test modes may be exploitable. Security boundaries may be violated. Firmware integrity may be compromised. Memory protection may be disabled.
Solution
Remove debug/scan mode overrides from security-critical lock logic. Require authentication before debug mode can override locks. Implement separate debug-only registers that don't affect security. Exclude debug signals from lock reset conditions. Fuse-disable debug lock override in production. Test lock behavior across all debug and test modes. Audit all paths that can affect lock state. Consider separate lock mechanisms for debug and production.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Bypass Protection Mechanism - Lock bit protections can be circumvented through debug or internal modes, enabling access and modification of system configuration even when lock should be active. |
Example Code
Vulnerable Code
// Vulnerable: Debug mode overrides lock protection
module vulnerable_debug_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire set_lock,
input wire scan_mode, // Manufacturing test mode
input wire debug_unlocked, // JTAG debug unlock
output reg [31:0] protected_config,
output reg lock_status
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
protected_config <= 32'h0;
lock_status <= 1'b0;
end
else begin
if (set_lock) begin
lock_status <= 1'b1;
end
// VULNERABLE: Debug modes bypass lock!
if (write_enable && (!lock_status || scan_mode || debug_unlocked)) begin
protected_config <= write_data;
end
end
end
// Attack: Enable scan_mode or debug_unlocked to bypass lock
endmodule
// Vulnerable: Lock cleared when entering debug mode
module vulnerable_debug_reset_lock (
input wire clk,
input wire rst_ni,
input wire jtag_unlock,
input wire [31:0] write_data,
input wire write_enable,
output reg [31:0] protected_config,
output reg [31:0] lock_register
);
integer j;
always @(posedge clk or negedge rst_ni) begin
// VULNERABLE: JTAG unlock clears lock registers!
if (~(rst_ni && ~jtag_unlock)) begin
for (j = 0; j < 32; j = j + 1) begin
lock_register[j] <= 1'b0; // Locks cleared!
end
end
else begin
if (write_enable && lock_register == 32'h0) begin
protected_config <= write_data;
end
end
end
// Attack: Use JTAG to assert jtag_unlock, clearing all locks
endmodule
// Vulnerable: Scan chain can modify lock state
module vulnerable_scan_lock (
input wire clk,
input wire reset_n,
input wire scan_enable,
input wire scan_in,
input wire [31:0] write_data,
input wire write_enable,
output reg [31:0] protected_config,
output reg lock_bit,
output wire scan_out
);
// Lock bit is in scan chain
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
lock_bit <= 1'b0;
protected_config <= 32'h0;
end
else if (scan_enable) begin
// VULNERABLE: Scan mode can shift in 0 to clear lock
lock_bit <= scan_in;
end
else begin
// Normal operation
if (write_enable && !lock_bit) begin
protected_config <= write_data;
end
end
end
assign scan_out = lock_bit;
endmodule
// Vulnerable: Firmware doesn't account for debug bypass
void vulnerable_security_init(void) {
// Set security configuration
configure_security_settings();
// Lock configuration
set_security_lock();
// Assumes config is protected
// But debug mode can bypass the lock!
}
// If attacker has debug access:
void attacker_with_debug(void) {
// Enter debug mode
enable_jtag_debug();
// Locks are now bypassed
// Modify protected config directly
*(volatile uint32_t*)PROTECTED_CONFIG = MALICIOUS_VALUE;
}
Fixed Code
// Fixed: Debug mode cannot override security locks
module secure_debug_lock (
input wire clk,
input wire reset_n,
input wire [31:0] write_data,
input wire write_enable,
input wire set_lock,
input wire scan_mode,
input wire debug_unlocked,
input wire debug_authenticated, // Proper debug authentication
output reg [31:0] protected_config,
output reg lock_status
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
protected_config <= 32'h0;
lock_status <= 1'b0;
end
else begin
if (set_lock) begin
lock_status <= 1'b1;
end
// FIXED: Lock is absolute - debug modes don't bypass
if (write_enable && !lock_status) begin
protected_config <= write_data;
end
// scan_mode and debug_unlocked do NOT affect lock
end
end
endmodule
// Fixed: Debug mode has separate registers, doesn't affect locks
module secure_debug_separation (
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,
input wire debug_mode,
output reg [31:0] protected_config, // Never bypassed
output reg [31:0] debug_scratch, // Debug-only register
output reg lock_status
);
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
protected_config <= 32'h0;
debug_scratch <= 32'h0;
lock_status <= 1'b0;
end
else begin
if (set_lock) begin
lock_status <= 1'b1;
end
if (write_enable) begin
case (register_select)
8'h00: begin
// Protected config - lock always enforced
if (!lock_status) begin
protected_config <= write_data;
end
end
8'h10: begin
// Debug scratch - separate, doesn't affect security
if (debug_mode) begin
debug_scratch <= write_data;
end
end
endcase
end
end
end
endmodule
// Fixed: Lock not in scan chain, cannot be modified by scan
module secure_scan_lock (
input wire clk,
input wire reset_n,
input wire scan_enable,
input wire scan_in,
input wire [31:0] write_data,
input wire write_enable,
input wire set_lock,
output reg [31:0] protected_config,
output reg lock_bit,
output wire scan_out
);
// Scannable register for testing (not security-critical)
reg [31:0] scan_register;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
lock_bit <= 1'b0;
protected_config <= 32'h0;
scan_register <= 32'h0;
end
else if (scan_enable) begin
// FIXED: Only scan_register is in scan chain
// Lock bit is NOT scannable
scan_register <= {scan_register[30:0], scan_in};
end
else begin
// Normal operation
if (set_lock) begin
lock_bit <= 1'b1; // Can only be set, not cleared by scan
end
if (write_enable && !lock_bit) begin
protected_config <= write_data;
end
end
end
// Scan out only includes non-security registers
assign scan_out = scan_register[31];
endmodule
// Fixed: JTAG unlock doesn't clear security locks
module secure_jtag_lock (
input wire clk,
input wire rst_ni,
input wire jtag_unlock,
input wire jtag_authenticated, // Requires authentication
input wire [31:0] write_data,
input wire write_enable,
input wire set_lock,
output reg [31:0] protected_config,
output reg [31:0] debug_config,
output reg security_lock,
output reg debug_lock
);
always @(posedge clk or negedge rst_ni) begin
if (!rst_ni) begin
protected_config <= 32'h0;
debug_config <= 32'h0;
security_lock <= 1'b0;
debug_lock <= 1'b0;
end
else begin
// FIXED: Security lock only cleared by full reset
// JTAG cannot clear security lock
if (set_lock) begin
security_lock <= 1'b1;
end
// Debug lock can be managed by authenticated JTAG
if (jtag_authenticated && jtag_unlock) begin
debug_lock <= 1'b0; // Only debug lock, not security lock
end
// Protected config always respects security lock
if (write_enable && !security_lock) begin
protected_config <= write_data;
end
// Debug config respects debug lock
if (write_enable && !debug_lock) begin
debug_config <= write_data;
end
end
end
endmodule
// Fixed: Firmware verifies debug cannot bypass locks
void secure_security_init(void) {
// Set security configuration
configure_security_settings();
// Lock configuration
set_security_lock();
// Verify debug mode cannot bypass
verify_debug_isolation();
}
bool verify_debug_isolation(void) {
bool secure = true;
// Enable debug mode
if (can_enable_debug()) {
enable_debug_mode();
// Verify lock is still effective
uint32_t before = *(volatile uint32_t*)PROTECTED_CONFIG;
*(volatile uint32_t*)PROTECTED_CONFIG = ~before;
uint32_t after = *(volatile uint32_t*)PROTECTED_CONFIG;
if (after != before) {
log_error("Debug mode bypasses security lock!");
secure = false;
}
disable_debug_mode();
}
// Test scan mode if available
if (can_enable_scan()) {
enable_scan_mode();
uint32_t lock_before = get_security_lock();
// Try to clear lock through scan
shift_scan_pattern(SCAN_CLEAR_LOCK_PATTERN);
uint32_t lock_after = get_security_lock();
if (lock_after != lock_before) {
log_error("Scan mode can modify security lock!");
secure = false;
}
disable_scan_mode();
}
return secure;
}
// Production fuse to disable debug bypass
void production_lockdown(void) {
// Blow fuse to permanently disable debug lock override
blow_debug_override_fuse();
// Verify fuse is blown
if (is_debug_override_enabled()) {
panic("Failed to disable debug override!");
}
}
CVE Examples
Debug mode lock bypass vulnerabilities have been found in various SoC designs where JTAG or scan mode could clear or bypass security locks.
Related CWEs
- CWE-667: Improper Locking (parent)
- CWE-1199: General Circuit and Logic Design Concerns (category member)
- CWE-1207: Debug and Test Problems (category member)
- CWE-1191: On-Chip Debug and Test Interface With Improper Access Control (related)
References
- MITRE Corporation. "CWE-1234: Hardware Internal or Debug Modes Allow Override of Locks." https://cwe.mitre.org/data/definitions/1234.html
- REF-1375: OpenPiton SoC Register Lock Implementation
- Debug Security Design Guidelines