Sensitive Information Uncleared Before Debug/Power State Transition
Description
Sensitive Information Uncleared Before Debug/Power State Transition occurs when a product fails to clear sensitive information during power or debug state transitions, leaving data accessible when it should no longer be available due to changed access restrictions. Devices operate across multiple power states (normal, low power, hibernate, deep sleep) and debug conditions. When transitioning between states, information that was accessible in the previous state may remain in memory or storage. If this sensitive data isn't removed before the transition, it can leak to unauthorized actors in the new state.
Risk
Uncleared sensitive information has severe security implications. Cryptographic keys may leak across states. Authentication credentials may be exposed. Debug access may reveal secrets. Hidden device capabilities may be unlocked. Confidential data may be compromised. Security boundaries may be violated. Attackers can exploit state transitions. Information disclosure enables further attacks.
Solution
During state transitions, remove information not needed in the next state before completing the transition. Implement automatic scrubbing of sensitive registers and memory during power transitions. Clear cryptographic keys before entering debug mode. Design state machines that enforce data clearing. Verify clearing through testing. Apply this mitigation during both architecture/design and implementation phases.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Sensitive data remains accessible after state transition. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Attackers can access data through state transitions. |
| Authentication | Scope: Authentication Gain Privileges - Credentials may be recovered from previous state. |
Example Code
Vulnerable Code
// Vulnerable: State transition without clearing sensitive data
module vulnerable_power_controller (
input wire clk,
input wire reset_n,
input wire [1:0] power_request,
output reg [1:0] current_state,
// Sensitive data
input wire [127:0] encryption_key,
output reg [127:0] key_register
);
// Power states
parameter ACTIVE = 2'b00;
parameter IDLE = 2'b01;
parameter SLEEP = 2'b10;
parameter DEBUG = 2'b11;
// VULNERABLE: Key remains in register across state transitions
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
current_state <= ACTIVE;
key_register <= 128'h0;
end
else begin
case (current_state)
ACTIVE: begin
// Load key in active state
key_register <= encryption_key;
if (power_request == SLEEP) begin
// VULNERABLE: Transition to sleep without clearing key
current_state <= SLEEP;
end
else if (power_request == DEBUG) begin
// VULNERABLE: Transition to debug without clearing key
current_state <= DEBUG;
// Key is still accessible in debug state!
end
end
SLEEP: begin
// VULNERABLE: Key still present in sleep state
// Attacker can probe memory while device sleeps
if (power_request == ACTIVE) begin
current_state <= ACTIVE;
end
end
DEBUG: begin
// VULNERABLE: Full key visible in debug state
// Debug interface can read key_register
if (power_request == ACTIVE) begin
current_state <= ACTIVE;
end
end
endcase
end
end
endmodule
// Vulnerable: Debug mode exposing sensitive information
module vulnerable_debug_access (
input wire clk,
input wire reset_n,
input wire debug_enable,
input wire [7:0] debug_addr,
output reg [31:0] debug_data,
// Internal sensitive state
input wire [255:0] private_key,
input wire [127:0] session_key,
input wire [63:0] user_credentials
);
// VULNERABLE: All internal state accessible in debug mode
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
debug_data <= 32'h0;
end
else if (debug_enable) begin
// VULNERABLE: No clearing of sensitive data before debug
case (debug_addr)
8'h00: debug_data <= private_key[31:0];
8'h01: debug_data <= private_key[63:32];
8'h02: debug_data <= private_key[95:64];
// ... full private key accessible
8'h10: debug_data <= session_key[31:0];
8'h14: debug_data <= user_credentials[31:0];
endcase
end
end
endmodule
// Vulnerable: Software state transition without clearing
#include <stdint.h>
typedef struct {
uint8_t aes_key[32];
uint8_t private_key[256];
uint8_t user_password[64];
uint8_t session_token[32];
} sensitive_data_t;
static sensitive_data_t secrets;
// VULNERABLE: Enter sleep without clearing secrets
void vulnerable_enter_sleep(void) {
// Save some state for resume
save_cpu_context();
// VULNERABLE: Secrets remain in memory during sleep
// RAM may be accessible to attacker
// Enter sleep mode
enter_low_power_mode();
// On wake, secrets are still present
}
// VULNERABLE: Enter debug mode without clearing secrets
void vulnerable_enter_debug(void) {
// VULNERABLE: No clearing before debug
// Enable debug interface
enable_jtag();
enable_debug_uart();
// Debugger can now access all memory including secrets!
}
// VULNERABLE: TPM specification violation
void vulnerable_tpm_power_transition(void) {
// VULNERABLE: Don't set required flags per TPM spec
// Failed authorization attempts not recorded after power loss
// Enter low power mode
tpm_enter_idle();
// Power loss occurs...
// On resume, authorization failure counter is reset
// Attacker gets unlimited password attempts!
}
Fixed Code
// Fixed: State transition with sensitive data clearing
module secure_power_controller (
input wire clk,
input wire reset_n,
input wire [1:0] power_request,
output reg [1:0] current_state,
input wire [127:0] encryption_key,
output reg [127:0] key_register,
output reg key_valid,
output reg transition_complete
);
// Power states
parameter ACTIVE = 2'b00;
parameter IDLE = 2'b01;
parameter SLEEP = 2'b10;
parameter DEBUG = 2'b11;
// State transition phases
reg [1:0] transition_phase;
parameter PHASE_IDLE = 2'b00;
parameter PHASE_CLEARING = 2'b01;
parameter PHASE_TRANSITION = 2'b10;
parameter PHASE_COMPLETE = 2'b11;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
current_state <= ACTIVE;
key_register <= 128'h0;
key_valid <= 1'b0;
transition_phase <= PHASE_IDLE;
transition_complete <= 1'b0;
end
else begin
case (current_state)
ACTIVE: begin
transition_complete <= 1'b0;
// Load key in active state
key_register <= encryption_key;
key_valid <= 1'b1;
if (power_request == SLEEP || power_request == DEBUG) begin
// FIXED: Enter clearing phase before transition
transition_phase <= PHASE_CLEARING;
end
end
endcase
// FIXED: Clearing phase
if (transition_phase == PHASE_CLEARING) begin
// Clear all sensitive data
key_register <= 128'h0;
key_valid <= 1'b0;
transition_phase <= PHASE_TRANSITION;
end
// Transition after clearing complete
if (transition_phase == PHASE_TRANSITION) begin
current_state <= power_request;
transition_phase <= PHASE_COMPLETE;
transition_complete <= 1'b1;
end
if (transition_phase == PHASE_COMPLETE) begin
transition_phase <= PHASE_IDLE;
end
end
end
endmodule
// Fixed: Debug mode with sensitive data protection
module secure_debug_access (
input wire clk,
input wire reset_n,
input wire debug_request,
input wire debug_authenticated,
input wire [7:0] debug_addr,
output reg [31:0] debug_data,
output reg debug_enabled,
// Internal sensitive state
input wire [255:0] private_key,
input wire [127:0] session_key,
input wire [63:0] user_credentials
);
// FIXED: Cleared copies for debug access
reg [255:0] cleared_private_key;
reg [127:0] cleared_session_key;
reg [63:0] cleared_credentials;
reg sensitive_cleared;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
debug_data <= 32'h0;
debug_enabled <= 1'b0;
sensitive_cleared <= 1'b0;
cleared_private_key <= 256'h0;
cleared_session_key <= 128'h0;
cleared_credentials <= 64'h0;
end
else begin
if (debug_request && debug_authenticated) begin
// FIXED: Clear sensitive data BEFORE enabling debug
if (!sensitive_cleared) begin
cleared_private_key <= 256'h0;
cleared_session_key <= 128'h0;
cleared_credentials <= 64'h0;
sensitive_cleared <= 1'b1;
end
else begin
debug_enabled <= 1'b1;
end
end
if (debug_enabled) begin
case (debug_addr)
// FIXED: Only provide cleared/sanitized data
8'h00: debug_data <= 32'hDEADBEEF; // Indicator
8'h10: debug_data <= 32'hDEADBEEF; // No real data
default: debug_data <= 32'h0;
endcase
end
// Exit debug mode
if (!debug_request) begin
debug_enabled <= 1'b0;
sensitive_cleared <= 1'b0;
end
end
end
endmodule
// Fixed: Power state machine with mandatory clearing
module secure_power_fsm (
input wire clk,
input wire reset_n,
input wire [1:0] requested_state,
input wire transition_request,
output reg [1:0] current_state,
output reg clearing_in_progress,
output reg transition_complete,
// Sensitive data interface
output reg clear_keys,
output reg clear_session,
output reg clear_cache,
input wire keys_cleared,
input wire session_cleared,
input wire cache_cleared
);
parameter ACTIVE = 2'b00;
parameter SLEEP = 2'b01;
parameter HIBERNATE = 2'b10;
parameter DEBUG = 2'b11;
reg [2:0] fsm_state;
parameter FSM_IDLE = 3'd0;
parameter FSM_START_CLEAR = 3'd1;
parameter FSM_WAIT_CLEAR = 3'd2;
parameter FSM_VERIFY_CLEAR = 3'd3;
parameter FSM_TRANSITION = 3'd4;
parameter FSM_COMPLETE = 3'd5;
reg [1:0] pending_state;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
current_state <= ACTIVE;
fsm_state <= FSM_IDLE;
clearing_in_progress <= 1'b0;
transition_complete <= 1'b0;
clear_keys <= 1'b0;
clear_session <= 1'b0;
clear_cache <= 1'b0;
end
else begin
case (fsm_state)
FSM_IDLE: begin
transition_complete <= 1'b0;
if (transition_request && requested_state != current_state) begin
pending_state <= requested_state;
fsm_state <= FSM_START_CLEAR;
end
end
FSM_START_CLEAR: begin
// FIXED: Initiate clearing of all sensitive data
clearing_in_progress <= 1'b1;
clear_keys <= 1'b1;
clear_session <= 1'b1;
clear_cache <= 1'b1;
fsm_state <= FSM_WAIT_CLEAR;
end
FSM_WAIT_CLEAR: begin
// FIXED: Wait for all clearing to complete
if (keys_cleared && session_cleared && cache_cleared) begin
clear_keys <= 1'b0;
clear_session <= 1'b0;
clear_cache <= 1'b0;
fsm_state <= FSM_VERIFY_CLEAR;
end
end
FSM_VERIFY_CLEAR: begin
// FIXED: Verify clearing was successful
// Could add additional verification here
fsm_state <= FSM_TRANSITION;
end
FSM_TRANSITION: begin
// FIXED: Only transition after clearing verified
current_state <= pending_state;
clearing_in_progress <= 1'b0;
fsm_state <= FSM_COMPLETE;
end
FSM_COMPLETE: begin
transition_complete <= 1'b1;
fsm_state <= FSM_IDLE;
end
endcase
end
end
endmodule
// Fixed: Software state transition with proper clearing
#include <stdint.h>
#include <string.h>
typedef struct {
uint8_t aes_key[32];
uint8_t private_key[256];
uint8_t user_password[64];
uint8_t session_token[32];
} sensitive_data_t;
static sensitive_data_t secrets;
// FIXED: Secure memory clear that won't be optimized out
static void secure_clear(volatile void* ptr, size_t size) {
volatile uint8_t* p = (volatile uint8_t*)ptr;
while (size--) {
*p++ = 0;
}
__asm__ volatile("" ::: "memory");
}
// FIXED: Enter sleep with proper clearing
void secure_enter_sleep(void) {
// FIXED: Clear sensitive data before sleep
secure_clear(&secrets.aes_key, sizeof(secrets.aes_key));
secure_clear(&secrets.private_key, sizeof(secrets.private_key));
secure_clear(&secrets.session_token, sizeof(secrets.session_token));
// Note: May keep user_password if needed for resume authentication
// FIXED: Clear hardware key registers
clear_hardware_key_registers();
// FIXED: Clear any caches
flush_and_invalidate_cache();
// Save minimal state for resume
save_cpu_context();
// Now safe to enter sleep
enter_low_power_mode();
}
// FIXED: Enter debug mode with sensitive data protection
void secure_enter_debug(void) {
// FIXED: Clear ALL sensitive data before enabling debug
secure_clear(&secrets, sizeof(secrets));
// FIXED: Clear hardware security state
clear_hardware_key_registers();
clear_secure_memory_regions();
zeroize_crypto_state();
// FIXED: Verify clearing
if (!verify_sensitive_data_cleared()) {
log_error("Failed to clear sensitive data");
secure_halt();
return;
}
// Now safe to enable debug
enable_jtag();
enable_debug_uart();
}
// FIXED: TPM compliant power transition
void secure_tpm_power_transition(void) {
// FIXED: Set required flags per TPM specification
tpm_set_state_save_flag();
// FIXED: Save authorization failure counter
save_auth_failure_counter();
// Enter low power mode
tpm_enter_idle();
// On resume, counter will be restored
// Brute force attacks still limited
}
// FIXED: Resume from sleep with verification
void secure_resume_from_sleep(void) {
// Verify we're in expected state
if (!verify_sleep_state()) {
secure_halt();
return;
}
// FIXED: Verify sensitive data was cleared
if (!verify_secrets_zero(&secrets.aes_key, sizeof(secrets.aes_key))) {
log_error("AES key not cleared during sleep");
secure_halt();
return;
}
// Restore CPU context
restore_cpu_context();
// Re-authenticate before restoring secrets
if (!authenticate_user()) {
secure_halt();
return;
}
// Now safe to reload secrets from secure storage
reload_secrets_from_secure_storage();
}
CVE Examples
- CVE-2020-12926: Product software failed to set required flags per TPM specifications, preventing failed authorization attempts from being recorded after power loss, enabling brute force attacks.
Related CWEs
- CWE-226: Sensitive Information in Resource Not Removed Before Reuse (parent)
- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (related)
- CWE-1207: Debug and Test Problems (category)
- CWE-1258: Exposure of Sensitive System Information Due to Uncleared Debug Information (related)
References
- MITRE Corporation. "CWE-1272: Sensitive Information Uncleared Before Debug/Power State Transition." https://cwe.mitre.org/data/definitions/1272.html
- TCG. "TPM 2.0 Library Specification"
- ARM. "TrustZone Power Management"