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

ImpactDetails
Access ControlScope: 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.


  • 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

  1. MITRE Corporation. "CWE-1234: Hardware Internal or Debug Modes Allow Override of Locks." https://cwe.mitre.org/data/definitions/1234.html
  2. REF-1375: OpenPiton SoC Register Lock Implementation
  3. Debug Security Design Guidelines