Improper Finite State Machines (FSMs) in Hardware Logic

Description

Improper Finite State Machines (FSMs) in Hardware Logic occurs when faulty finite state machines in the hardware logic allow an attacker to put the system in an undefined state, to cause a denial of service (DoS) or gain privileges on the victim's system. The security and functionality of systems depend heavily on FSM implementation. These machines indicate system security states, and numerous secure data operations rely on their accuracy. Faulty FSM designs that omit certain states can allow attackers to drive systems into unstable, unrecoverable states.

Risk

Improper FSM implementation has severe security implications. Systems may enter undefined states. Denial of service may occur through state manipulation. Security state transitions may be bypassed. Privilege escalation may be possible. System crashes or restarts may occur. Deadlocks may render systems unresponsive. Authentication states may be bypassable. Access control decisions may be compromised.

Solution

Define all possible states and handle all unused states through default statements. Ensure that system defaults to a secure state. Use formal verification to check FSM completeness. Implement unreachable state detection and recovery. Add watchdog timers for FSM timeout. Log unexpected state transitions. Test all possible input combinations. Use encoding that detects invalid states.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Crash, Exit, or Restart - Faulty FSMs can cause unexpected system states leading to crashes or restarts.
Access ControlScope: Access Control

Gain Privileges or Assume Identity - FSM vulnerabilities may allow privilege escalation or bypassing security states.

Example Code

Vulnerable Code

// Vulnerable: FSM with missing state handlers

module vulnerable_auth_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] input_event,
    output reg [1:0] current_state,
    output reg authenticated,
    output reg access_granted
);

    // States
    parameter IDLE = 2'b00;
    parameter AUTHENTICATING = 2'b01;
    parameter AUTHENTICATED = 2'b10;
    // State 2'b11 undefined - VULNERABLE!

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= IDLE;
            authenticated <= 1'b0;
            access_granted <= 1'b0;
        end
        else begin
            case (current_state)
                IDLE: begin
                    if (input_event == 3'h1) begin
                        current_state <= AUTHENTICATING;
                    end
                end

                AUTHENTICATING: begin
                    if (input_event == 3'h2) begin
                        current_state <= AUTHENTICATED;
                        authenticated <= 1'b1;
                    end
                    else if (input_event == 3'h3) begin
                        current_state <= IDLE;
                    end
                end

                AUTHENTICATED: begin
                    access_granted <= 1'b1;
                    if (input_event == 3'h4) begin
                        current_state <= IDLE;
                        authenticated <= 1'b0;
                        access_granted <= 1'b0;
                    end
                end

                // VULNERABLE: No handler for state 2'b11
                // If reached, FSM behavior is undefined!
            endcase
        end
    end

endmodule

// Vulnerable: Input combinations not fully handled
module vulnerable_security_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] security_input,
    output reg [2:0] security_state
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            security_state <= 3'h0;
        end
        else begin
            case (security_input)
                3'h0: security_state <= 3'h0;  // Safe state
                3'h1: security_state <= 3'h1;  // State 1
                3'h2: security_state <= 3'h2;  // State 2
                3'h3: security_state <= 3'h3;  // State 3
                3'h4: security_state <= 3'h4;  // State 4
                3'h5: security_state <= 3'h5;  // State 5
                // VULNERABLE: Inputs 3'h6 and 3'h7 not handled!
                // System goes to undefined state
            endcase
        end
    end

endmodule

// Vulnerable: FSM can enter deadlock state
module vulnerable_protocol_fsm (
    input wire clk,
    input wire reset_n,
    input wire request,
    input wire response,
    input wire timeout,
    output reg [2:0] state,
    output reg transaction_complete
);

    parameter IDLE = 3'h0;
    parameter WAIT_ACK = 3'h1;
    parameter PROCESSING = 3'h2;
    parameter WAIT_RESPONSE = 3'h3;
    parameter COMPLETE = 3'h4;
    parameter ERROR = 3'h5;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            transaction_complete <= 1'b0;
        end
        else begin
            case (state)
                IDLE: begin
                    if (request) state <= WAIT_ACK;
                end

                WAIT_ACK: begin
                    if (response) state <= PROCESSING;
                    else if (timeout) state <= ERROR;
                end

                PROCESSING: begin
                    state <= WAIT_RESPONSE;
                end

                WAIT_RESPONSE: begin
                    if (response) begin
                        state <= COMPLETE;
                        transaction_complete <= 1'b1;
                    end
                    // VULNERABLE: No timeout handling - can deadlock here!
                end

                COMPLETE: begin
                    state <= IDLE;
                    transaction_complete <= 1'b0;
                end

                ERROR: begin
                    // VULNERABLE: No exit from ERROR state!
                    // System stuck in error forever
                end
            endcase
        end
    end

endmodule
// Vulnerable: Software FSM with missing cases

typedef enum {
    STATE_INIT,
    STATE_AUTHENTICATING,
    STATE_AUTHENTICATED,
    STATE_ADMIN,
    STATE_COUNT  // 4 states
} system_state_t;

system_state_t current_state = STATE_INIT;

void vulnerable_state_machine(int event) {
    switch (current_state) {
        case STATE_INIT:
            if (event == EVENT_LOGIN) {
                current_state = STATE_AUTHENTICATING;
            }
            break;

        case STATE_AUTHENTICATING:
            if (event == EVENT_AUTH_SUCCESS) {
                current_state = STATE_AUTHENTICATED;
            } else if (event == EVENT_AUTH_FAIL) {
                current_state = STATE_INIT;
            }
            break;

        case STATE_AUTHENTICATED:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_REQUEST) {
                current_state = STATE_ADMIN;
            }
            break;

        // VULNERABLE: STATE_ADMIN not handled!
        // If reached, no state transitions possible

        // VULNERABLE: No default case for invalid states
    }
}

Fixed Code

// Fixed: FSM with all states handled

module secure_auth_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] input_event,
    output reg [1:0] current_state,
    output reg authenticated,
    output reg access_granted,
    output reg fsm_error
);

    // States
    parameter IDLE = 2'b00;
    parameter AUTHENTICATING = 2'b01;
    parameter AUTHENTICATED = 2'b10;
    parameter ERROR = 2'b11;  // Explicitly handle undefined state

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            current_state <= IDLE;
            authenticated <= 1'b0;
            access_granted <= 1'b0;
            fsm_error <= 1'b0;
        end
        else begin
            case (current_state)
                IDLE: begin
                    authenticated <= 1'b0;
                    access_granted <= 1'b0;
                    if (input_event == 3'h1) begin
                        current_state <= AUTHENTICATING;
                    end
                end

                AUTHENTICATING: begin
                    if (input_event == 3'h2) begin
                        current_state <= AUTHENTICATED;
                        authenticated <= 1'b1;
                    end
                    else if (input_event == 3'h3) begin
                        current_state <= IDLE;
                    end
                end

                AUTHENTICATED: begin
                    access_granted <= 1'b1;
                    if (input_event == 3'h4) begin
                        current_state <= IDLE;
                        authenticated <= 1'b0;
                        access_granted <= 1'b0;
                    end
                end

                // FIXED: Explicit handler for undefined/error state
                default: begin
                    // Return to safe state
                    current_state <= IDLE;
                    authenticated <= 1'b0;
                    access_granted <= 1'b0;
                    fsm_error <= 1'b1;  // Log error
                end
            endcase
        end
    end

endmodule

// Fixed: All input combinations handled
module secure_security_fsm (
    input wire clk,
    input wire reset_n,
    input wire [2:0] security_input,
    output reg [2:0] security_state,
    output reg invalid_input
);

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            security_state <= 3'h0;
            invalid_input <= 1'b0;
        end
        else begin
            invalid_input <= 1'b0;

            case (security_input)
                3'h0: security_state <= 3'h0;
                3'h1: security_state <= 3'h1;
                3'h2: security_state <= 3'h2;
                3'h3: security_state <= 3'h3;
                3'h4: security_state <= 3'h4;
                3'h5: security_state <= 3'h5;
                // FIXED: Handle all remaining inputs
                default: begin
                    security_state <= 3'h0;  // Return to safe state
                    invalid_input <= 1'b1;   // Flag invalid input
                end
            endcase
        end
    end

endmodule

// Fixed: FSM with timeout and recovery
module secure_protocol_fsm (
    input wire clk,
    input wire reset_n,
    input wire request,
    input wire response,
    input wire timeout,
    input wire error_ack,
    output reg [2:0] state,
    output reg transaction_complete,
    output reg error_flag
);

    parameter IDLE = 3'h0;
    parameter WAIT_ACK = 3'h1;
    parameter PROCESSING = 3'h2;
    parameter WAIT_RESPONSE = 3'h3;
    parameter COMPLETE = 3'h4;
    parameter ERROR = 3'h5;

    // Timeout counter for WAIT_RESPONSE
    reg [15:0] wait_counter;
    parameter WAIT_TIMEOUT = 16'hFFFF;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            state <= IDLE;
            transaction_complete <= 1'b0;
            error_flag <= 1'b0;
            wait_counter <= 16'h0;
        end
        else begin
            case (state)
                IDLE: begin
                    error_flag <= 1'b0;
                    wait_counter <= 16'h0;
                    if (request) state <= WAIT_ACK;
                end

                WAIT_ACK: begin
                    if (response) state <= PROCESSING;
                    else if (timeout) state <= ERROR;
                end

                PROCESSING: begin
                    state <= WAIT_RESPONSE;
                    wait_counter <= 16'h0;
                end

                WAIT_RESPONSE: begin
                    wait_counter <= wait_counter + 1;
                    if (response) begin
                        state <= COMPLETE;
                        transaction_complete <= 1'b1;
                    end
                    // FIXED: Timeout handling for WAIT_RESPONSE
                    else if (wait_counter >= WAIT_TIMEOUT) begin
                        state <= ERROR;
                        error_flag <= 1'b1;
                    end
                end

                COMPLETE: begin
                    state <= IDLE;
                    transaction_complete <= 1'b0;
                end

                ERROR: begin
                    error_flag <= 1'b1;
                    // FIXED: Exit from ERROR state with acknowledgment
                    if (error_ack) begin
                        state <= IDLE;
                        error_flag <= 1'b0;
                    end
                end

                // FIXED: Handle undefined states
                default: begin
                    state <= IDLE;
                    error_flag <= 1'b1;
                end
            endcase
        end
    end

endmodule
// Fixed: Software FSM with all cases handled

typedef enum {
    STATE_INIT,
    STATE_AUTHENTICATING,
    STATE_AUTHENTICATED,
    STATE_ADMIN,
    STATE_ERROR,
    STATE_COUNT
} system_state_t;

system_state_t current_state = STATE_INIT;

void secure_state_machine(int event) {
    switch (current_state) {
        case STATE_INIT:
            if (event == EVENT_LOGIN) {
                current_state = STATE_AUTHENTICATING;
            }
            break;

        case STATE_AUTHENTICATING:
            if (event == EVENT_AUTH_SUCCESS) {
                current_state = STATE_AUTHENTICATED;
            } else if (event == EVENT_AUTH_FAIL) {
                current_state = STATE_INIT;
            }
            break;

        case STATE_AUTHENTICATED:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_REQUEST) {
                current_state = STATE_ADMIN;
            }
            break;

        // FIXED: STATE_ADMIN handled
        case STATE_ADMIN:
            if (event == EVENT_LOGOUT) {
                current_state = STATE_INIT;
            } else if (event == EVENT_ADMIN_DONE) {
                current_state = STATE_AUTHENTICATED;
            }
            break;

        case STATE_ERROR:
            // Error recovery
            if (event == EVENT_RESET) {
                current_state = STATE_INIT;
            }
            break;

        // FIXED: Default case for invalid states
        default:
            log_error("Invalid FSM state: %d", current_state);
            current_state = STATE_INIT;  // Return to safe state
            break;
    }
}

// FSM validation function
bool validate_fsm_state(void) {
    if (current_state >= STATE_COUNT) {
        log_security_event("FSM in invalid state: %d", current_state);
        current_state = STATE_INIT;
        return false;
    }
    return true;
}

CVE Examples

FSM vulnerabilities have been found in various hardware designs where undefined states could be reached, leading to security bypasses or denial of service.


  • CWE-684: Incorrect Provision of Specified Functionality (parent)
  • CWE-1199: General Circuit and Logic Design Concerns (category member)

References

  1. MITRE Corporation. "CWE-1245: Improper Finite State Machines (FSMs) in Hardware Logic." https://cwe.mitre.org/data/definitions/1245.html
  2. REF-1060: Farimah Farahmandi and Prabhat Mishra on FSM Anomaly Detection