Unautorisierte Fehlerinjektion kann Hardware-Redundanz beeinträchtigen

Beschreibung

Unautorisierte Fehlerinjektion kann Hardware-Redundanz beeinträchtigen tritt auf, wenn ein unautorisierter Agent Fehler in einen redundanten Block injizieren kann, um dem System die Redundanz zu entziehen oder es in einen degradierten Betriebsmodus zu versetzen. Hardware-Designer implementieren redundante Blöcke, um Leistung und Zuverlässigkeit bei Ausfall primärer Komponenten aufrechtzuerhalten. Wenn das Design jedoch unautorisierten Parteien erlaubt, Fehler in diese redundanten Systeme zu injizieren, können sie Backup-Pfade deaktivieren, das System in degradierte Betriebszustände zwingen und potenziell nachfolgende Angriffe ermöglichen.

Risiko

Unautorisierte Fehlerinjektion hat schwerwiegende Auswirkungen. Redundanz wird eliminiert und ermöglicht Einzelpunktausfälle. Sicherheitsmargen werden entfernt. Fehlertoleranz wird umgangen. Systemzuverlässigkeit wird beeintrachtigt. Nachfolgende Angriffe werden ermöglicht. Denial of Service durch induzierte Ausfalle. Qualitätsverschlechterung. Leistungsbeeinträchtigung. Erhöhter Ressourcenverbrauch. Hohe Wahrscheinlichkeit, wenn Fehlerinjektionsschnittstellen keine Zugangskontrolle haben.

Lösung

Fehlerinjektion während der normalen Laufzeit in der Architektur- und Designphase verhindern. Zugangskontrollen für Fehlerinjektionsschnittstellen implementieren. Fehlerinjektion in Standard-Betriebsmodi während der Implementierungsphase verbieten. Zugangskontrollen für Fehlerinjektionsmechanismen hinzufügen. Zugangskontrollen über ungeschützte Schnittstellen während der Integrationsphase schichten.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

Denial of Service durch Systemabsturze, Neustarts und Instabilität.
IntegritatBereich: Integritat

Qualitätsverschlechterung und reduzierte Leistung durch deaktivierte Redundanz.

Beispielcode

Verwundbarer Code

// VERWUNDBAR: Redundantes System ohne Fehlerinjektionsschutz

module vulnerable_redundant_system (
    input  wire        clk,
    input  wire        rst_n,

    // Primäre und redundante Berechnungspfade
    input  wire [31:0] data_in,
    input  wire        compute_start,

    // Fehlerinjektionsschnittstelle (VERWUNDBAR: kein Schutz)
    input  wire        inject_error_primary,
    input  wire        inject_error_redundant,
    input  wire [7:0]  error_pattern,

    output reg  [31:0] result,
    output reg         result_valid,
    output reg         redundancy_mismatch
);

    reg [31:0] primary_result;
    reg [31:0] redundant_result;

    // Primärer Berechnungspfad
    always @(posedge clk) begin
        if (compute_start) begin
            primary_result <= compute_function(data_in);

            // VERWUNDBAR: Fehler kann ohne Autorisierung injiziert werden
            if (inject_error_primary) begin
                primary_result <= primary_result ^ {24'b0, error_pattern};
            end
        end
    end

    // Redundanter Berechnungspfad
    always @(posedge clk) begin
        if (compute_start) begin
            redundant_result <= compute_function(data_in);

            // VERWUNDBAR: Fehlerinjektion in redundanten Pfad
            if (inject_error_redundant) begin
                redundant_result <= redundant_result ^ {24'b0, error_pattern};
            end
        end
    end

    // Vergleichslogik
    always @(posedge clk) begin
        redundancy_mismatch <= (primary_result != redundant_result);

        if (!redundancy_mismatch) begin
            result <= primary_result;
            result_valid <= 1'b1;
        end else begin
            // System wechselt in degradierten Modus
            result_valid <= 1'b0;
        end
    end

    // Angriff:
    // 1. Kontinuierliche Fehler in redundanten Pfad injizieren
    // 2. System erkennt ständig Abweichungen
    // 3. Redundanz erscheint ausgefallen
    // 4. System arbeitet im degradierten Modus
    // 5. Ein einzelner Fehler führt nun zum vollständigen Ausfall

endmodule

// VERWUNDBAR: ECC-Speicher ohne Fehlerinjektionsschutz
module vulnerable_ecc_memory (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [15:0] addr,
    input  wire [31:0] data_in,
    input  wire        write_en,
    input  wire        read_en,

    // VERWUNDBAR: Fehlerinjektion für Tests
    input  wire        inject_single_bit_error,
    input  wire        inject_multi_bit_error,
    input  wire [4:0]  error_bit_position,

    output reg  [31:0] data_out,
    output reg         single_bit_corrected,
    output reg         multi_bit_detected
);

    reg [38:0] memory [0:65535];  // 32 Daten + 7 ECC-Bits

    always @(posedge clk) begin
        if (write_en) begin
            memory[addr] <= {compute_ecc(data_in), data_in};
        end

        if (read_en) begin
            reg [38:0] raw_data = memory[addr];

            // VERWUNDBAR: Fehlerinjektion ohne Zugangskontrolle
            if (inject_single_bit_error) begin
                raw_data[error_bit_position] <= ~raw_data[error_bit_position];
            end

            if (inject_multi_bit_error) begin
                raw_data[error_bit_position] <= ~raw_data[error_bit_position];
                raw_data[error_bit_position + 1] <= ~raw_data[error_bit_position + 1];
            end

            // ECC-Dekodierung
            {single_bit_corrected, multi_bit_detected, data_out} <= ecc_decode(raw_data);
        end
    end

    // Angriff: Kontinuierlich Multi-Bit-Fehler injizieren
    // System protokolliert Ausfalle, kann ECC-Schutz deaktivieren
    // Speicher ist nun anfällig für tatsachliche Fehler

endmodule

Sichere Lösung

// SICHER: Redundantes System mit geschützter Fehlerinjektion

module secure_redundant_system (
    input  wire        clk,
    input  wire        rst_n,

    // Primäre und redundante Berechnungspfade
    input  wire [31:0] data_in,
    input  wire        compute_start,

    // Fehlerinjektionsschnittstelle (SICHER: geschützt)
    input  wire        inject_error_primary,
    input  wire        inject_error_redundant,
    input  wire [7:0]  error_pattern,
    input  wire        test_mode_enabled,      // SICHER: Lebenszyklus-Signal
    input  wire        privileged_access,      // SICHER: Zugangskontrolle
    input  wire        boot_complete,          // SICHER: Laufzeitindikator

    output reg  [31:0] result,
    output reg         result_valid,
    output reg         redundancy_mismatch,
    output reg         injection_denied
);

    reg [31:0] primary_result;
    reg [31:0] redundant_result;

    // SICHER: Fehlerinjektionskontrolle
    wire error_injection_allowed = test_mode_enabled &&
                                   privileged_access &&
                                   !boot_complete;

    // Primärer Berechnungspfad
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            primary_result <= 32'b0;
            injection_denied <= 1'b0;
        end else if (compute_start) begin
            primary_result <= compute_function(data_in);
            injection_denied <= 1'b0;

            // SICHER: Fehlerinjektion nur in erlaubten Modi
            if (inject_error_primary) begin
                if (error_injection_allowed) begin
                    primary_result <= primary_result ^ {24'b0, error_pattern};
                end else begin
                    // SICHER: Unautorisierte Injektion ablehnen
                    injection_denied <= 1'b1;
                end
            end
        end
    end

    // Redundanter Berechnungspfad (ähnlicher Schutz)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            redundant_result <= 32'b0;
        end else if (compute_start) begin
            redundant_result <= compute_function(data_in);

            if (inject_error_redundant && error_injection_allowed) begin
                redundant_result <= redundant_result ^ {24'b0, error_pattern};
            end
        end
    end

    // SICHER: Vergleich mit Protokollierung von Injektionsversuchen
    always @(posedge clk) begin
        redundancy_mismatch <= (primary_result != redundant_result);

        if (!redundancy_mismatch) begin
            result <= primary_result;
            result_valid <= 1'b1;
        end else begin
            result_valid <= 1'b0;
        end
    end

    // SICHER: Testmodus nach dem Booten sperren
    always @(posedge boot_complete) begin
        // Testmodus wird nach dem Booten automatisch deaktiviert
        // Fehlerinjektion ist nicht mehr möglich
    end

endmodule

// SICHER: ECC-Speicher mit geschützter Fehlerinjektion
module secure_ecc_memory (
    input  wire        clk,
    input  wire        rst_n,
    input  wire [15:0] addr,
    input  wire [31:0] data_in,
    input  wire        write_en,
    input  wire        read_en,

    // SICHER: Geschutzte Fehlerinjektion
    input  wire        inject_single_bit_error,
    input  wire        inject_multi_bit_error,
    input  wire [4:0]  error_bit_position,
    input  wire        test_mode,
    input  wire        secure_debug_enabled,   // SICHER: Debug-Authentifizierung
    input  wire        manufacturing_mode,      // SICHER: Lebenszyklus

    output reg  [31:0] data_out,
    output reg         single_bit_corrected,
    output reg         multi_bit_detected,
    output reg         injection_blocked
);

    reg [38:0] memory [0:65535];

    // SICHER: Fehlerinjektion nur während Fertigungstest
    wire injection_allowed = manufacturing_mode &&
                            (test_mode || secure_debug_enabled);

    // SICHER: Ratenbegrenzung für Fehlerinjektion
    reg [7:0] injection_count;
    localparam MAX_INJECTIONS = 100;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            injection_count <= 8'b0;
            injection_blocked <= 1'b0;
        end else begin
            injection_blocked <= 1'b0;

            if (write_en) begin
                memory[addr] <= {compute_ecc(data_in), data_in};
            end

            if (read_en) begin
                reg [38:0] raw_data;
                raw_data = memory[addr];

                // SICHER: Geschutzte Fehlerinjektion
                if (inject_single_bit_error || inject_multi_bit_error) begin
                    if (injection_allowed && injection_count < MAX_INJECTIONS) begin
                        if (inject_single_bit_error) begin
                            raw_data[error_bit_position] = ~raw_data[error_bit_position];
                        end

                        if (inject_multi_bit_error) begin
                            raw_data[error_bit_position] = ~raw_data[error_bit_position];
                            raw_data[error_bit_position + 1] = ~raw_data[error_bit_position + 1];
                        end

                        injection_count <= injection_count + 1;
                    end else begin
                        // SICHER: Unautorisierte oder übermäßige Injektion blockieren
                        injection_blocked <= 1'b1;
                    end
                end

                // ECC-Dekodierung
                {single_bit_corrected, multi_bit_detected, data_out} <= ecc_decode(raw_data);
            end
        end
    end

    // SICHER: Injektionsfahigkeit in Produktion sperren
    always @(posedge clk) begin
        if (!manufacturing_mode) begin
            injection_count <= MAX_INJECTIONS;  // Effektiv deaktivieren
        end
    end

endmodule

// SICHER: Redundanzmanager mit Angriffserkennung
module secure_redundancy_manager (
    input  wire        clk,
    input  wire        rst_n,

    // Redundante Eingange
    input  wire [31:0] input_a,
    input  wire [31:0] input_b,
    input  wire [31:0] input_c,  // Dreifach modulare Redundanz
    input  wire        inputs_valid,

    // Status
    output reg  [31:0] voted_output,
    output reg         output_valid,
    output reg         single_failure,
    output reg         double_failure,
    output reg         attack_detected
);

    // SICHER: Ausfallmuster verfolgen, um Angriffe zu erkennen
    reg [15:0] failure_history;
    reg [7:0] failure_count_a;
    reg [7:0] failure_count_b;
    reg [7:0] failure_count_c;

    localparam ATTACK_THRESHOLD = 10;  // Ausfalle im Zeitfenster

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            failure_history <= 16'b0;
            failure_count_a <= 8'b0;
            failure_count_b <= 8'b0;
            failure_count_c <= 8'b0;
            attack_detected <= 1'b0;
        end else if (inputs_valid) begin
            // Dreifach modulare Redundanz-Abstimmung
            reg match_ab = (input_a == input_b);
            reg match_bc = (input_b == input_c);
            reg match_ac = (input_a == input_c);

            // SICHER: Verfolgen, welche Kanäle ausfallen
            if (!match_ab && !match_ac) failure_count_a <= failure_count_a + 1;
            if (!match_ab && !match_bc) failure_count_b <= failure_count_b + 1;
            if (!match_ac && !match_bc) failure_count_c <= failure_count_c + 1;

            // SICHER: Angriffsmuster erkennen (konsistente Ausfalle auf einem Kanal)
            if (failure_count_a > ATTACK_THRESHOLD ||
                failure_count_b > ATTACK_THRESHOLD ||
                failure_count_c > ATTACK_THRESHOLD) begin
                attack_detected <= 1'b1;
            end

            // Abstimmungslogik
            if (match_ab || match_ac) begin
                voted_output <= input_a;
                output_valid <= 1'b1;
                single_failure <= !(match_ab && match_ac && match_bc);
                double_failure <= 1'b0;
            end else if (match_bc) begin
                voted_output <= input_b;
                output_valid <= 1'b1;
                single_failure <= 1'b1;
                double_failure <= 1'b0;
            end else begin
                // Alle drei stimmen nicht überein
                output_valid <= 1'b0;
                double_failure <= 1'b1;
            end
        end
    end

    // SICHER: Periodisches Zurücksetzen der Ausfallzahler (gleitendes Fenster)
    reg [15:0] window_counter;
    localparam WINDOW_SIZE = 10000;

    always @(posedge clk) begin
        window_counter <= window_counter + 1;
        if (window_counter >= WINDOW_SIZE) begin
            window_counter <= 16'b0;
            failure_count_a <= failure_count_a >> 1;  // Abklingen
            failure_count_b <= failure_count_b >> 1;
            failure_count_c <= failure_count_c >> 1;
        end
    end

endmodule

CVE-Beispiele

  • CVE-2019-11135: TSX Asynchronous Abort (TAA) ermöglichte Fehlerinjektion, die CPU-Redundanzmechanismen beeintrachtigt.
  • CVE-2020-0549: L1D Eviction Sampling-Schwachstelle, die durch Fehlerinjektion ausnutzbar ist.

Verwandte CWEs

  • CWE-284: Improper Access Control (ubergeordnet)
  • CWE-1198: Privilege Separation and Access Control Issues (Kategorie)
  • CAPEC-624: Hardware Fault Injection
  • CAPEC-625: Mobile Device Fault Injection

Referenzen

  1. MITRE Corporation. "CWE-1334: Unauthorized Error Injection Can Degrade Hardware Redundancy." https://cwe.mitre.org/data/definitions/1334.html
  2. IEEE. "Fault Tolerance in Digital Systems"
  3. NASA. "Fault Tolerant Design Handbook"