Improper Lock Behavior After Power State Transition

Description

Improper Lock Behavior After Power State Transition occurs when register lock bit protection disables changes to system configuration once the bit is set, but these protections become programmable after power state transitions. Devices typically rely on trusted firmware or software to configure critical system resources and then lock them via a programmable lock bit. The problem occurs when lock bits become unlocked, protected register values reset, or locks become reprogrammable following power state transitions like entering or waking from low-power sleep modes. This creates a vulnerability window where attackers could modify protected configuration.

Risk

Lock behavior issues after power transitions have severe security implications. Security configurations may be reset to unlocked defaults. Protected registers may become writable after sleep. Attackers can force low-power transitions to clear locks. Boot security settings may be bypassed. Debug interfaces may be re-enabled after wake. Memory protection may be disabled. Firmware integrity checks may be circumvented. Critical system settings may be modified.

Solution

Review security lock bit protections across all supported power state transitions. Ensure locks persist or are securely re-established after power transitions. Initialize lock registers to locked (not unlocked) state on reset. Test lock programming flow and properties during pre-silicon and post-silicon validation including power transition scenarios. Implement lock state persistence across power states. Verify security configuration after wake from low-power modes.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Modify Memory - System configuration protected by lock bit can be modified after power state transitions, compromising access control. High likelihood.

Example Code

Vulnerable Code

// Vulnerable: Locks reset to unlocked state after power transition

module vulnerable_power_lock (
    input wire clk,
    input wire reset_n,
    input wire sleep_mode,
    input wire wake_signal,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire set_lock,
    output reg [31:0] config_register,
    output reg [31:0] lock_register
);

    reg power_state;  // 0 = active, 1 = sleep

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            config_register <= 32'h0;
            lock_register <= 32'h0;  // VULNERABLE: Unlocked by default
            power_state <= 1'b0;
        end
        else if (sleep_mode) begin
            // Entering sleep mode
            power_state <= 1'b1;
        end
        else if (wake_signal && power_state) begin
            // VULNERABLE: Waking from sleep resets locks!
            power_state <= 1'b0;
            lock_register <= 32'h0;  // Locks cleared on wake!
            // Config preserved but now unprotected
        end
        else begin
            // Normal operation
            if (set_lock) begin
                lock_register <= 32'hFFFFFFFF;
            end

            if (write_enable && lock_register == 32'h0) begin
                config_register <= write_data;
            end
        end
    end

    // Attack scenario:
    // 1. System boots, locks are set
    // 2. Attacker triggers sleep mode
    // 3. Attacker triggers wake
    // 4. Locks are cleared, config is modifiable!

endmodule

// Vulnerable: Lock defaults to 0 (unlocked) on any reset
module vulnerable_reset_lock (
    input wire clk,
    input wire reset_n,
    input wire power_on_reset,
    input wire soft_reset,
    input wire sleep_exit_reset,
    input wire [31:0] write_data,
    input wire write_enable,
    output reg [31:0] protected_config,
    output reg [31:0] lock_status
);

    // Combined reset signal
    wire any_reset = !reset_n | power_on_reset | soft_reset | sleep_exit_reset;

    always @(posedge clk) begin
        if (any_reset) begin
            // VULNERABLE: All resets clear locks
            lock_status <= 32'h0;
            protected_config <= 32'h0;
        end
        else begin
            if (write_enable && lock_status == 32'h0) begin
                protected_config <= write_data;
            end
        end
    end

endmodule
// Vulnerable: Firmware doesn't verify locks after power transition

void vulnerable_sleep_handler(void) {
    // Save state and enter sleep
    save_context();
    enter_low_power_mode();

    // ... system sleeps ...

    // Wake up
    restore_context();

    // VULNERABLE: Doesn't verify security locks after wake
    // Assumes locks are still set - they may not be!

    continue_operation();
}

void vulnerable_init(void) {
    // Configure and lock
    configure_memory_protection();
    set_security_locks();

    // Assume locks persist - vulnerable assumption
}

Fixed Code

// Fixed: Locks persist across power state transitions

module secure_power_lock (
    input wire clk,
    input wire reset_n,
    input wire power_on_reset,  // Full power-on only
    input wire sleep_mode,
    input wire wake_signal,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire set_lock,
    output reg [31:0] config_register,
    output reg [31:0] lock_register,
    output reg lock_violation
);

    reg power_state;
    reg [31:0] saved_config;
    reg [31:0] saved_locks;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            // Only full reset clears everything
            config_register <= 32'h0;
            lock_register <= 32'hFFFFFFFF;  // FIXED: Locked by default!
            power_state <= 1'b0;
            lock_violation <= 1'b0;
        end
        else if (sleep_mode && !power_state) begin
            // Entering sleep - save state
            power_state <= 1'b1;
            saved_config <= config_register;
            saved_locks <= lock_register;
        end
        else if (wake_signal && power_state) begin
            // FIXED: Restore locks on wake, don't clear them
            power_state <= 1'b0;
            lock_register <= saved_locks;
            config_register <= saved_config;
        end
        else begin
            // Normal operation
            // Lock can only transition to more restrictive
            if (set_lock) begin
                lock_register <= lock_register | 32'hFFFFFFFF;
            end

            if (write_enable) begin
                if (lock_register == 32'h0) begin
                    config_register <= write_data;
                end else begin
                    lock_violation <= 1'b1;  // Log violation
                end
            end
        end
    end

endmodule

// Fixed: Locks default to locked, only power-on-reset clears
module secure_reset_lock (
    input wire clk,
    input wire reset_n,
    input wire power_on_reset,
    input wire soft_reset,
    input wire sleep_exit_reset,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire set_lock,
    output reg [31:0] protected_config,
    output reg [31:0] lock_status
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n || power_on_reset) begin
            // Only power-on reset clears locks
            lock_status <= 32'hFFFFFFFF;  // Locked by default
            protected_config <= 32'h0;
        end
        else if (soft_reset) begin
            // Soft reset: preserve locks, optionally reset config
            // lock_status preserved
            // protected_config optionally preserved or reset based on policy
        end
        else if (sleep_exit_reset) begin
            // Sleep exit: preserve both locks and config
            // Everything preserved
        end
        else begin
            // Normal operation
            if (set_lock) begin
                lock_status <= 32'hFFFFFFFF;
            end

            if (write_enable && lock_status == 32'h0) begin
                protected_config <= write_data;
            end
        end
    end

endmodule

// Fixed: Lock state preserved in always-on domain
module secure_always_on_lock (
    input wire main_clk,
    input wire always_on_clk,
    input wire main_reset_n,
    input wire power_on_reset,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire set_lock,
    output wire [31:0] protected_config,
    output wire lock_active
);

    // Lock stored in always-on power domain
    // Persists through main domain power cycles
    reg lock_state_aon;  // Always-on flip-flop

    always @(posedge always_on_clk or negedge power_on_reset) begin
        if (!power_on_reset) begin
            lock_state_aon <= 1'b1;  // Locked by default
        end
        else if (set_lock) begin
            lock_state_aon <= 1'b1;
        end
        // Lock persists through sleep - no clear path
    end

    assign lock_active = lock_state_aon;

endmodule
// Fixed: Firmware verifies locks after power transitions

void secure_sleep_handler(void) {
    // Save state
    save_context();

    // Verify locks before sleep
    uint32_t locks_before = get_lock_status();

    enter_low_power_mode();

    // ... system sleeps ...

    // Wake up
    restore_context();

    // FIXED: Verify locks after wake
    uint32_t locks_after = get_lock_status();

    if (locks_after != locks_before) {
        // Locks changed during sleep - security violation!
        log_security_event("Lock state changed after wake");

        // Re-establish locks
        restore_security_locks(locks_before);

        // Verify restoration
        if (get_lock_status() != locks_before) {
            panic("Cannot restore security locks!");
        }
    }

    // Verify protected config wasn't modified
    verify_protected_configuration();

    continue_operation();
}

void secure_init(void) {
    // Check if this is power-on or resume
    if (is_cold_boot()) {
        // Full initialization
        configure_memory_protection();
        set_security_locks();
    } else {
        // Resuming from low power - verify locks
        if (!verify_security_locks()) {
            // Locks not set - re-initialize
            log_warning("Security locks missing after resume");
            configure_memory_protection();
            set_security_locks();
        }
    }

    // Final verification
    if (!verify_all_security_settings()) {
        panic("Security configuration invalid!");
    }
}

// Verification function
bool verify_security_locks(void) {
    uint32_t expected_locks = REQUIRED_LOCK_MASK;
    uint32_t actual_locks = get_lock_status();

    if ((actual_locks & expected_locks) != expected_locks) {
        log_error("Missing locks: expected 0x%08X, got 0x%08X",
                  expected_locks, actual_locks);
        return false;
    }

    // Test that locked registers cannot be written
    uint32_t test_addr = LOCKED_CONFIG_REG;
    uint32_t before = *(volatile uint32_t*)test_addr;
    *(volatile uint32_t*)test_addr = ~before;
    uint32_t after = *(volatile uint32_t*)test_addr;

    if (after != before) {
        log_error("Locked register was modified!");
        return false;
    }

    return true;
}

CVE Examples

  • Power state transition vulnerabilities have been found in various SoC designs where security locks were cleared or reset during sleep/wake cycles
  • CAPEC-166: Force the System to Reset Values

  • CWE-667: Improper Locking (parent)
  • CWE-1199: General Circuit and Logic Design Concerns (category member)
  • CWE-1206: Power, Clock, Thermal, and Reset Concerns (category member)
  • CWE-1231: Improper Prevention of Lock Bit Modification (related)

References

  1. MITRE Corporation. "CWE-1232: Improper Lock Behavior After Power State Transition." https://cwe.mitre.org/data/definitions/1232.html
  2. Hack@DAC'21 OpenPiton SoC Security Analysis
  3. Power Management Security Guidelines