Improper Handling of Faults that Lead to Instruction Skips

Description

Improper Handling of Faults that Lead to Instruction Skips occurs when the device is missing or incorrectly implements circuitry or sensors that detect and mitigate the skipping of security-critical CPU instructions when they occur. Operating condition changes can cause unexpected hardware behavior including instruction skipping. Security-sensitive conditional branches (like password verification) implemented as single CPU instructions become vulnerable when skipped, potentially reversing the branch logic. Attackers exploit this through fault injection techniques to manipulate hardware operating conditions.

Risk

Instruction skip vulnerabilities have severe implications. Security checks bypassed. Authentication defeated. Conditional branches reversed. Protection mechanisms circumvented. Unauthorized code execution. Arbitrary memory access. Privilege escalation enabled. Cryptographic operations corrupted. High likelihood when fault injection protections are absent.

Solution

Design safe-failure mechanisms for out-of-tolerance input conditions during architecture and design phase. Implement redundant operations with majority voting. Add fault detection canaries or monitoring circuits. Ensure mitigation strength accounts for detection latency. Design critical-secret wiping upon fault detection.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Protection mechanisms bypassed through instruction skip.
IntegrityScope: Integrity

Execution logic altered enabling unauthorized actions.
AuthenticationScope: Authentication

Authentication checks skipped allowing unauthorized access.
ConfidentialityScope: Confidentiality

Data protection bypassed through skipped security checks.

Example Code

Vulnerable Code

// Vulnerable: Single-instruction security check

#include <stdint.h>
#include <stdbool.h>

// VULNERABLE: Single comparison instruction
bool vulnerable_verify_password(const char* input, const char* stored) {
    // VULNERABLE: strcmp compiles to single comparison
    // Fault injection can skip this, making result always equal
    if (strcmp(input, stored) != 0) {
        return false;  // Can be skipped!
    }
    return true;
}

// VULNERABLE: Single conditional branch
bool vulnerable_check_signature(uint8_t* data, uint8_t* sig) {
    uint8_t computed[32];
    sha256(data, strlen(data), computed);

    // VULNERABLE: Single branch instruction
    if (memcmp(computed, sig, 32) != 0) {
        return false;  // This instruction can be skipped!
    }
    return true;

    // Attack: Fault injection skips the "return false"
    // Invalid signature is accepted
}

// VULNERABLE: Security check with single branch
void vulnerable_secure_boot(void) {
    uint8_t* firmware = load_firmware();
    uint8_t* signature = load_signature();

    // VULNERABLE: Single conditional
    if (!verify_signature(firmware, signature)) {
        halt_system();  // Can be skipped!
    }

    // If halt is skipped, unsigned firmware executes
    execute_firmware(firmware);
}

// VULNERABLE: Access control with single check
int vulnerable_read_secret(int user_privilege) {
    // VULNERABLE: Single branch
    if (user_privilege < ADMIN_LEVEL) {
        return ERROR_ACCESS_DENIED;  // Skippable!
    }

    return secret_data;
}
// Vulnerable: Hardware without fault detection

module vulnerable_security_check (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] password_input,
    input  wire [31:0] password_stored,
    input  wire        check_password,
    output reg         access_granted
);

    // VULNERABLE: Single comparison
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            access_granted <= 1'b0;
        end else if (check_password) begin
            // VULNERABLE: Single comparison susceptible to glitch
            if (password_input == password_stored) begin
                access_granted <= 1'b1;
            end else begin
                access_granted <= 1'b0;
            end
        end
    end

    // Attack: Glitch during comparison
    // Comparison skipped, access_granted gets unexpected value

endmodule

// Vulnerable: No voltage/clock glitch detection
module vulnerable_processor (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] instruction,
    // No glitch detection inputs!
    output reg  [31:0] result
);

    // VULNERABLE: No monitoring of operating conditions
    // Voltage glitch can cause instruction skip

    always @(posedge clk) begin
        // Execute instruction normally
        // No fault detection
        case (instruction[31:24])
            8'h00: result <= instruction[23:0];  // MOV
            8'h01: result <= result + instruction[23:0];  // ADD
            // Security-critical conditional can be skipped
            8'hFF: if (instruction[0]) result <= 32'hDEAD;  // Branch
        endcase
    end

endmodule

Fixed Code

// Fixed: Fault-resistant security checks

#include <stdint.h>
#include <stdbool.h>

// FIXED: Redundant verification with multiple checks
bool secure_verify_password(const char* input, const char* stored) {
    volatile int result1, result2, result3;

    // FIXED: Multiple redundant comparisons
    result1 = strcmp(input, stored);
    result2 = strcmp(input, stored);
    result3 = strcmp(input, stored);

    // FIXED: Majority voting
    int match_count = 0;
    if (result1 == 0) match_count++;
    if (result2 == 0) match_count++;
    if (result3 == 0) match_count++;

    // FIXED: All three must agree
    if (match_count != 3 && match_count != 0) {
        // FIXED: Fault detected - results inconsistent
        fault_detected();
        return false;
    }

    return (match_count == 3);
}

// FIXED: Redundant signature verification
bool secure_check_signature(uint8_t* data, uint8_t* sig) {
    uint8_t computed1[32], computed2[32];

    // FIXED: Compute hash twice
    sha256(data, strlen(data), computed1);
    sha256(data, strlen(data), computed2);

    // FIXED: Verify redundant computations match
    if (memcmp(computed1, computed2, 32) != 0) {
        fault_detected();
        return false;
    }

    // FIXED: Multiple comparison methods
    volatile int cmp1 = memcmp(computed1, sig, 32);
    volatile int cmp2 = secure_compare(computed1, sig, 32);
    volatile int cmp3 = memcmp(computed2, sig, 32);

    // FIXED: Check consistency
    if (!((cmp1 == 0 && cmp2 == 0 && cmp3 == 0) ||
          (cmp1 != 0 && cmp2 != 0 && cmp3 != 0))) {
        fault_detected();
        return false;
    }

    return (cmp1 == 0);
}

// FIXED: Secure boot with redundant checks
void secure_boot_protected(void) {
    uint8_t* firmware = load_firmware();
    uint8_t* signature = load_signature();

    // FIXED: Multiple verification passes
    volatile bool pass1 = verify_signature(firmware, signature);
    volatile bool pass2 = verify_signature(firmware, signature);
    volatile bool pass3 = verify_signature(firmware, signature);

    // FIXED: Flow integrity variable
    volatile uint32_t flow_check = 0;

    if (!pass1) flow_check |= 0x01;
    if (!pass2) flow_check |= 0x02;
    if (!pass3) flow_check |= 0x04;

    // FIXED: Verify all checks ran and agreed
    if (flow_check != 0x00 && flow_check != 0x07) {
        // Inconsistent results - fault detected
        fault_detected();
        secure_halt();
    }

    if (flow_check == 0x07) {
        // All verifications failed
        secure_halt();
    }

    // FIXED: Additional canary check before execution
    if (flow_check != 0x00 || !pass1 || !pass2 || !pass3) {
        secure_halt();
    }

    execute_firmware(firmware);
}

// FIXED: Access control with redundancy
int secure_read_secret(int user_privilege) {
    volatile int priv1 = user_privilege;
    volatile int priv2 = user_privilege;

    // FIXED: Double-check with canary
    volatile uint32_t canary = 0xDEADBEEF;

    int check1 = (priv1 >= ADMIN_LEVEL);
    int check2 = (priv2 >= ADMIN_LEVEL);

    // FIXED: Verify canary wasn't corrupted
    if (canary != 0xDEADBEEF) {
        fault_detected();
        return ERROR_FAULT_DETECTED;
    }

    // FIXED: Both checks must agree
    if (check1 != check2) {
        fault_detected();
        return ERROR_FAULT_DETECTED;
    }

    if (!check1) {
        return ERROR_ACCESS_DENIED;
    }

    return secret_data;
}
// Fixed: Hardware with fault detection and redundancy

module secure_security_check (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] password_input,
    input  wire [31:0] password_stored,
    input  wire        check_password,

    // FIXED: Glitch detection inputs
    input  wire        voltage_ok,
    input  wire        clock_ok,
    input  wire        temp_ok,

    output reg         access_granted,
    output reg         fault_detected
);

    // FIXED: Redundant comparison registers
    reg match1, match2, match3;
    reg [31:0] password_copy1, password_copy2;

    // FIXED: Operating condition monitoring
    wire conditions_ok = voltage_ok && clock_ok && temp_ok;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            access_granted <= 1'b0;
            fault_detected <= 1'b0;
            match1 <= 1'b0;
            match2 <= 1'b0;
            match3 <= 1'b0;
        end else begin
            fault_detected <= 1'b0;

            // FIXED: Check operating conditions
            if (!conditions_ok) begin
                fault_detected <= 1'b1;
                access_granted <= 1'b0;
            end else if (check_password) begin
                // FIXED: Redundant comparisons
                password_copy1 <= password_input;
                password_copy2 <= password_input;

                match1 <= (password_input == password_stored);
                match2 <= (password_copy1 == password_stored);
                match3 <= (password_copy2 == password_stored);

                // FIXED: Majority voting with consistency check
                if (match1 && match2 && match3) begin
                    access_granted <= 1'b1;
                end else if (!match1 && !match2 && !match3) begin
                    access_granted <= 1'b0;
                end else begin
                    // FIXED: Inconsistent results = fault
                    fault_detected <= 1'b1;
                    access_granted <= 1'b0;
                end
            end
        end
    end

endmodule

// Fixed: Processor with glitch detection
module secure_processor (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [31:0] instruction,

    // FIXED: Glitch detection signals
    input  wire        voltage_monitor_ok,
    input  wire        clock_monitor_ok,
    input  wire [11:0] temperature,

    output reg  [31:0] result,
    output reg         fault_detected
);

    // FIXED: Temperature thresholds
    localparam TEMP_LOW = 12'h100;
    localparam TEMP_HIGH = 12'hD00;

    // FIXED: Combined condition check
    wire conditions_safe = voltage_monitor_ok &&
                          clock_monitor_ok &&
                          (temperature > TEMP_LOW) &&
                          (temperature < TEMP_HIGH);

    // FIXED: Instruction execution with monitoring
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            result <= 32'b0;
            fault_detected <= 1'b0;
        end else begin
            // FIXED: Block execution if conditions unsafe
            if (!conditions_safe) begin
                fault_detected <= 1'b1;
                result <= 32'b0;  // Safe output
            end else begin
                fault_detected <= 1'b0;

                case (instruction[31:24])
                    8'h00: result <= instruction[23:0];
                    8'h01: result <= result + instruction[23:0];
                    8'hFF: begin
                        // FIXED: Security branch with redundant check
                        if (instruction[0] && instruction[0]) begin
                            result <= 32'hDEAD;
                        end
                    end
                    default: result <= result;
                endcase
            end
        end
    end

endmodule

CVE Examples

  • CVE-2019-15894: Fault injection bypassing verification mode enabling arbitrary code execution.
  • CVE-2020-0069: Instruction skip allowing secure boot bypass in MediaTek processors.

  • CWE-1384: Improper Handling of Physical or Environmental Conditions (parent)
  • CWE-1247: Improper Protection Against Voltage and Clock Glitches (related)
  • CWE-1206: Power, Clock, Thermal, and Reset Concerns (category)
  • CWE-1388: Physical Access Issues and Concerns (category)
  • CAPEC-624: Hardware Fault Injection
  • CAPEC-625: Mobile Device Fault Injection

References

  1. MITRE Corporation. "CWE-1332: Improper Handling of Faults that Lead to Instruction Skips." https://cwe.mitre.org/data/definitions/1332.html
  2. Riscure. "Fault Injection Attacks and Countermeasures"
  3. ARM. "Security Considerations for Cortex-M Processors"