Hardware Allows Activation of Test or Debug Logic at Runtime
Description
Hardware Allows Activation of Test or Debug Logic at Runtime occurs when during runtime, the hardware allows for test or debug logic (feature) to be activated, which allows for changing the state of the hardware. This activation capability creates security risks by enabling adversaries to modify system behavior and access sensitive information. Examples include read/write access to system data through test modes, data modification via error injection during bus operations, and unauthorized access to system secrets.
Risk
Runtime test/debug activation has severe implications. Memory modification possible. Unauthorized reads enabled. Denial of service through instability. Unauthorized code execution. Privilege escalation. Protection mechanism bypass. Execution logic altered. Performance degraded. Reliability issues. Hardware state compromise. High likelihood when test modes remain accessible.
Solution
Insert restrictions on when the hardware's test or debug features can be activated. Prevent privileged mode access during normal operations. Limit test/debug activation to specific windows such as boot or configuration stages. Disable such features completely during runtime operations. Use hardware fuses to permanently disable test features in production. Require authentication for debug activation.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality System secrets readable through debug interfaces. |
| Integrity | Scope: Integrity Memory and execution state modifiable via test modes. |
| Availability | Scope: Availability Denial of service through crashes, instability, or resource exhaustion. |
Example Code
Vulnerable Code
// Vulnerable: Test mode activatable at runtime
module vulnerable_test_controller (
input wire clk,
input wire rst_n,
input wire test_mode_enable, // Software-controllable!
input wire [31:0] test_addr,
input wire [31:0] test_wdata,
input wire test_write,
input wire test_read,
// System interfaces
output reg [31:0] sys_addr,
output reg [31:0] sys_wdata,
output reg sys_write,
output reg sys_read,
output reg [31:0] sys_rdata,
// Internal debug access
output reg debug_mem_access,
output reg error_inject_enable
);
// VULNERABLE: Test mode can be enabled at any time
// No restrictions on when or who can enable it
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
debug_mem_access <= 1'b0;
error_inject_enable <= 1'b0;
end else begin
// VULNERABLE: Test mode grants special capabilities
if (test_mode_enable) begin
// VULNERABLE: Direct memory access bypassing protection
sys_addr <= test_addr;
sys_wdata <= test_wdata;
sys_write <= test_write;
sys_read <= test_read;
// VULNERABLE: Debug features enabled at runtime
debug_mem_access <= 1'b1;
error_inject_enable <= 1'b1;
end else begin
debug_mem_access <= 1'b0;
error_inject_enable <= 1'b0;
end
end
end
// Attack:
// 1. Malicious software sets test_mode_enable = 1
// 2. Debug memory access enabled
// 3. Attacker reads/writes protected memory
// 4. Error injection corrupts system state
endmodule
// Vulnerable: Debug logic accessible during normal operation
module vulnerable_cpu_debug (
input wire clk,
input wire rst_n,
input wire debug_request, // VULNERABLE: Always available
input wire [31:0] debug_addr,
input wire [31:0] debug_data,
input wire debug_write,
// CPU internals (should be protected)
output reg [31:0] pc_override,
output reg halt_cpu,
output reg single_step,
output reg [31:0] register_write_data,
output reg [4:0] register_write_addr,
output reg register_write_en
);
// VULNERABLE: No lifecycle state check
// Debug accessible even after secure boot completes
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
halt_cpu <= 1'b0;
single_step <= 1'b0;
register_write_en <= 1'b0;
end else if (debug_request) begin
// VULNERABLE: Debug commands processed at runtime
case (debug_addr[7:0])
8'h00: begin
// Halt CPU
halt_cpu <= debug_data[0];
end
8'h04: begin
// Override program counter
pc_override <= debug_data; // VULNERABLE!
end
8'h08: begin
// Write CPU register
register_write_addr <= debug_data[4:0];
register_write_data <= debug_data;
register_write_en <= 1'b1; // VULNERABLE!
end
8'h0C: begin
// Enable single-step mode
single_step <= debug_data[0];
end
endcase
end
end
// VULNERABLE: No authentication required
// VULNERABLE: No lifecycle check (production vs development)
endmodule
// Vulnerable: BIST accessible at runtime
module vulnerable_bist_controller (
input wire clk,
input wire rst_n,
input wire bist_start, // Software-triggerable
input wire [7:0] bist_pattern,
output reg bist_running,
output reg bist_complete,
output reg bist_pass
);
reg [31:0] memory [0:1023];
reg [9:0] test_addr;
// VULNERABLE: BIST can be started at any time
// This can corrupt memory during normal operation!
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bist_running <= 1'b0;
bist_complete <= 1'b0;
end else if (bist_start && !bist_running) begin
// VULNERABLE: BIST destroys memory contents!
bist_running <= 1'b1;
test_addr <= 10'b0;
end else if (bist_running) begin
// VULNERABLE: Overwrites live memory with test patterns
memory[test_addr] <= {4{bist_pattern}};
test_addr <= test_addr + 1;
if (test_addr == 10'h3FF) begin
bist_complete <= 1'b1;
bist_running <= 1'b0;
end
end
end
// Attack:
// Trigger BIST during runtime to corrupt critical data
// Causes denial of service or exploitable state
endmodule
Fixed Code
// Fixed: Test mode with lifecycle restrictions
module secure_test_controller (
input wire clk,
input wire rst_n,
input wire test_mode_request,
input wire [31:0] test_addr,
input wire [31:0] test_wdata,
input wire test_write,
input wire test_read,
// Lifecycle signals
input wire boot_complete, // Set when boot finishes
input wire production_mode_fuse, // OTP fuse for production
// System interfaces
output reg [31:0] sys_addr,
output reg [31:0] sys_wdata,
output reg sys_write,
output reg sys_read,
output reg [31:0] sys_rdata,
// Debug access (restricted)
output reg debug_mem_access,
output reg error_inject_enable,
output reg test_mode_denied
);
// FIXED: Test mode only allowed during specific lifecycle stages
wire test_allowed = !boot_complete && // Only before boot completes
!production_mode_fuse; // Only in development mode
// FIXED: Actual test mode state
reg test_mode_active;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
test_mode_active <= 1'b0;
debug_mem_access <= 1'b0;
error_inject_enable <= 1'b0;
test_mode_denied <= 1'b0;
end else begin
test_mode_denied <= 1'b0;
// FIXED: Check if test mode activation is allowed
if (test_mode_request) begin
if (test_allowed) begin
test_mode_active <= 1'b1;
end else begin
// FIXED: Deny test mode after boot or in production
test_mode_active <= 1'b0;
test_mode_denied <= 1'b1;
end
end
// FIXED: Test features only active when allowed
if (test_mode_active && test_allowed) begin
sys_addr <= test_addr;
sys_wdata <= test_wdata;
sys_write <= test_write;
sys_read <= test_read;
debug_mem_access <= 1'b1;
error_inject_enable <= 1'b1;
end else begin
// FIXED: Disable test features when not allowed
debug_mem_access <= 1'b0;
error_inject_enable <= 1'b0;
test_mode_active <= 1'b0;
end
end
end
// FIXED: Force disable test mode once boot completes
always @(posedge boot_complete) begin
test_mode_active <= 1'b0;
end
endmodule
// Fixed: Debug logic with authentication and lifecycle check
module secure_cpu_debug (
input wire clk,
input wire rst_n,
input wire debug_request,
input wire [31:0] debug_addr,
input wire [31:0] debug_data,
input wire debug_write,
// FIXED: Lifecycle and authentication
input wire debug_authenticated, // Challenge-response completed
input wire development_mode, // From fuse
input wire [1:0] lifecycle_state, // 00=dev, 01=prod, 10=rma, 11=scrap
// CPU internals
output reg [31:0] pc_override,
output reg halt_cpu,
output reg single_step,
output reg [31:0] register_write_data,
output reg [4:0] register_write_addr,
output reg register_write_en,
output reg debug_denied
);
// FIXED: Debug allowed only with proper authentication and lifecycle
localparam LC_DEV = 2'b00;
localparam LC_PROD = 2'b01;
localparam LC_RMA = 2'b10;
wire debug_allowed;
// FIXED: Different capabilities per lifecycle state
assign debug_allowed = (lifecycle_state == LC_DEV) ||
(lifecycle_state == LC_RMA && debug_authenticated);
// Production mode never allows debug
// FIXED: Limit capabilities even when allowed
wire full_debug = (lifecycle_state == LC_DEV);
wire limited_debug = (lifecycle_state == LC_RMA && debug_authenticated);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
halt_cpu <= 1'b0;
single_step <= 1'b0;
register_write_en <= 1'b0;
debug_denied <= 1'b0;
end else begin
register_write_en <= 1'b0;
debug_denied <= 1'b0;
if (debug_request) begin
if (!debug_allowed) begin
// FIXED: Deny debug in production
debug_denied <= 1'b1;
end else begin
case (debug_addr[7:0])
8'h00: begin
// Halt CPU - allowed in debug modes
halt_cpu <= debug_data[0];
end
8'h04: begin
// PC override - only in full debug
if (full_debug) begin
pc_override <= debug_data;
end else begin
debug_denied <= 1'b1;
end
end
8'h08: begin
// Register write - only in full debug
if (full_debug) begin
register_write_addr <= debug_data[4:0];
register_write_data <= debug_data;
register_write_en <= 1'b1;
end else begin
debug_denied <= 1'b1;
end
end
8'h0C: begin
// Single-step - allowed in limited debug
single_step <= debug_data[0];
end
default: begin
debug_denied <= 1'b1;
end
endcase
end
end
end
end
endmodule
// Fixed: BIST with runtime lockout
module secure_bist_controller (
input wire clk,
input wire rst_n,
input wire bist_start,
input wire [7:0] bist_pattern,
input wire boot_complete, // FIXED: Lifecycle signal
input wire bist_unlock_fuse, // FIXED: Special unlock fuse
output reg bist_running,
output reg bist_complete,
output reg bist_pass,
output reg bist_denied
);
reg [31:0] memory [0:1023];
reg [9:0] test_addr;
// FIXED: BIST only allowed before boot completes
wire bist_allowed = !boot_complete || bist_unlock_fuse;
// FIXED: Lock BIST after it completes
reg bist_locked;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bist_running <= 1'b0;
bist_complete <= 1'b0;
bist_locked <= 1'b0;
bist_denied <= 1'b0;
end else begin
bist_denied <= 1'b0;
if (bist_start && !bist_running) begin
// FIXED: Check if BIST is allowed
if (bist_allowed && !bist_locked) begin
bist_running <= 1'b1;
test_addr <= 10'b0;
end else begin
// FIXED: Deny BIST after boot or when locked
bist_denied <= 1'b1;
end
end else if (bist_running) begin
// Run BIST (only reaches here if allowed)
memory[test_addr] <= {4{bist_pattern}};
test_addr <= test_addr + 1;
if (test_addr == 10'h3FF) begin
bist_complete <= 1'b1;
bist_running <= 1'b0;
// FIXED: Lock BIST after completion
bist_locked <= 1'b1;
end
end
end
end
// FIXED: Also lock when boot completes
always @(posedge boot_complete) begin
if (!bist_unlock_fuse) begin
bist_locked <= 1'b1;
end
end
endmodule
CVE Examples
- CVE-2021-33150: Processor allows test or debug logic activation at runtime.
- CVE-2021-0146: Processor debug logic enables privilege escalation during operation.
- CVE-2019-0090: Intel CSME test mode accessible after secure boot.
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1207: Debug and Test Problems (category)
- CWE-1244: Internal Asset Exposed to Unsafe Debug Access (related)
- CWE-1243: Sensitive Non-Volatile Information Not Protected During Debug (related)
References
- MITRE Corporation. "CWE-1313: Hardware Allows Activation of Test or Debug Logic at Runtime." https://cwe.mitre.org/data/definitions/1313.html
- NIST. "Platform Firmware Resiliency Guidelines"
- ARM. "CoreSight Debug and Trace Architecture"