Hardware-Logik enthält Race Conditions

Beschreibung

Race Conditions in Hardware-Logik treten auf, wenn eine Race Condition in der Hardware-Logik die Sicherheitsgarantien des Systems untergräbt. Race Conditions in Logikschaltungen entstehen, wenn Logikgatter Eingangssignale empfangen, die vom gleichen Ursprung stammen, aber unterschiedliche Pfade durchlaufen, wodurch sich die Eingänge zu leicht unterschiedlichen Zeitpunkten ändern. Dies erzeugt Timing-Fehler oder Glitches, die dazu führen, dass Ausgänge vor der Stabilisierung in unerwünschte Zustände wechseln. Wenn diese Fehler in der Zugriffskontrolllogik oder in sicherheitskritischen endlichen Zustandsautomaten auftreten, können Angreifer diese ausnutzen, um bestehende Schutzmechanismen zu umgehen.

Risiko

Race Conditions in Hardware haben schwerwiegende Sicherheitsauswirkungen. Sicherheitsschutzmechanismen werden während Glitches umgangen. Privilege Escalation ist möglich. Ausführungslogik kann verändert werden. Zugriffskontrollentscheidungen werden verfälscht. Zustandsautomaten geraten in unerwartete Zustände. Metastabilitätsprobleme in der Sicherheitslogik. Angreifer können Operationen zeitlich so abstimmen, dass sie Race Conditions ausnutzen. Physische Angriffe können Race Conditions hervorrufen.

Lösung

Verwenden Sie Entwurfspraktiken, die Race Conditions erkennen und eliminieren, wie z.B. Karnaugh-Diagramme, während der Architektur- und Entwurfsphase. Implementieren Sie Logikredundanz entlang sicherheitskritischer Pfade. Setzen Sie als Standard einen sicheren Zustand, in dem der Zugriff für nicht vertrauenswürdige Agenten verweigert wird, um Metastabilitätsprobleme zu vermeiden. Fügen Sie Konsenslogik hinzu, um Glitches zu eliminieren. Registrieren Sie Eingänge der sicherheitskritischen Logik, um Änderungen während laufender Operationen zu verhindern.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismen werden während der Race-Condition-Fenster umgangen.
AutorisierungBereich: Autorisierung

Privilege Escalation oder Identitätsübernahme durch Timing-Angriffe.
IntegritätBereich: Integrität

Ausführungslogik wird während Glitches verändert.

Beispielcode und Lösung

Verwundbarer Code

// VERWUNDBAR: Multiplexer mit Glitch (Race Condition)

module vulnerable_glitch_mux (
    input  wire in0,
    input  wire in1,
    input  wire sel,
    output wire z
);

    wire not_sel, and_out1, and_out2;

    // VERWUNDBAR: Erzeugt Glitch im Signal z
    // Wenn sich sel ändert, gibt es ein kurzes Fenster, in dem sowohl
    // and_out1 als auch and_out2 fälschlicherweise high oder low sein könnten

    assign not_sel = ~sel;
    assign and_out1 = not_sel & in0;
    assign and_out2 = sel & in1;
    assign z = and_out1 | and_out2;  // VERWUNDBAR: Glitch tritt hier auf

    // Race Condition:
    // Wenn sel von 0->1 wechselt:
    // 1. sel wird 1
    // 2. not_sel ist noch 1 (Ausbreitungsverzögerung)
    // 3. Kurzer Moment: and_out1=in0, and_out2=in1 (beide könnten 1 sein!)
    // 4. z wechselt kurzzeitig in unerwarteten Zustand
    // 5. Stabilisiert sich schließlich auf korrektem Wert

    // Wenn z Zugriffsentscheidungen steuert, könnte ein kurzer Glitch Zugriff ermöglichen

endmodule

// VERWUNDBAR: Sicherheitsentscheidung mit Race Condition
module vulnerable_access_control (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        user_request,
    input  wire        admin_request,
    input  wire        is_admin,
    output wire        access_granted
);

    wire user_access, admin_access;

    // VERWUNDBAR: Race zwischen is_admin und Request-Signalen
    assign user_access = user_request & ~is_admin;
    assign admin_access = admin_request & is_admin;
    assign access_granted = user_access | admin_access;

    // Angriff: is_admin umschalten, während der Request aktiv ist
    // Glitch könnte Admin-Zugriff für Nicht-Admin-Benutzer gewähren

endmodule

// VERWUNDBAR: DMA mit Race Condition bei PMP-Signalen
module vulnerable_dma_pmp (
    input  wire        clk_i,
    input  wire        rst_ni,
    input  wire [7:0][15:0] pmpcfg_i,     // PMP-Konfiguration
    input  wire [15:0][53:0] pmpaddr_i,   // PMP-Adressen
    input  wire [2:0]  dma_ctrl_reg,
    input  wire [31:0] dma_src_addr,
    input  wire [31:0] dma_dst_addr,
    output reg         dma_transfer_valid
);

    localparam CTRL_IDLE = 3'b000;
    localparam CTRL_SETUP = 3'b001;
    localparam CTRL_TRANSFER = 3'b010;
    localparam CTRL_DONE = 3'b011;

    wire pmp_allow;

    // VERWUNDBAR: Direkte Verwendung asynchroner PMP-Signale
    // pmpaddr_i und pmpcfg_i können sich während der DMA-Operation ändern

    pmp_checker u_pmp (
        .cfg(pmpcfg_i),          // VERWUNDBAR: Direkte Verbindung
        .addr(pmpaddr_i),        // VERWUNDBAR: Kann sich während der Übertragung ändern
        .check_addr(dma_src_addr),
        .allow(pmp_allow)
    );

    always @(posedge clk_i or negedge rst_ni) begin
        if (!rst_ni) begin
            dma_transfer_valid <= 1'b0;
        end else begin
            case (dma_ctrl_reg)
                CTRL_SETUP: begin
                    // VERWUNDBAR: PMP wird beim Setup geprüft
                    // Aber Signale können sich während der Übertragung ändern
                    if (pmp_allow) begin
                        dma_transfer_valid <= 1'b1;
                    end
                end

                CTRL_TRANSFER: begin
                    // VERWUNDBAR: Keine erneute Prüfung während der Übertragung
                    // Angreifer kann PMP-Konfiguration nach der initialen Prüfung ändern,
                    // um auf privilegierten Speicher zuzugreifen
                end

                CTRL_DONE: begin
                    dma_transfer_valid <= 1'b0;
                end
            endcase
        end
    end

    // Angriff:
    // 1. PMP so setzen, dass Zugriff auf unprivilegierten Bereich erlaubt ist
    // 2. DMA-Übertragung starten (besteht die Prüfung)
    // 3. Während der Übertragung PMP auf privilegierten Bereich umstellen
    // 4. DMA fährt mit initialem "Erlaubt"-Status auf privilegierten Speicher fort!

endmodule

// VERWUNDBAR: Zustandsautomat mit Race Condition
module vulnerable_security_fsm (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        auth_signal,
    input  wire        access_request,
    output reg         secure_access
);

    // VERWUNDBAR: Kombinatorische Sicherheitsentscheidung
    // Anfällig für Race Conditions bei Eingängen

    localparam STATE_LOCKED = 2'b00;
    localparam STATE_AUTH   = 2'b01;
    localparam STATE_OPEN   = 2'b10;

    reg [1:0] state;

    // VERWUNDBAR: Direkte kombinatorische Logik für den Zugriff
    always @(*) begin
        // Race Condition wenn auth_signal und access_request sich ändern
        secure_access = (state == STATE_OPEN) ||
                        (auth_signal && access_request);  // GLITCH!
    end

endmodule

Sichere Lösung

// SICHER: Glitch-freier Multiplexer mit Konsenslogik

module secure_glitch_free_mux (
    input  wire in0,
    input  wire in1,
    input  wire sel,
    output wire z
);

    wire not_sel, and_out1, and_out2;

    assign not_sel = ~sel;
    assign and_out1 = not_sel & in0;
    assign and_out2 = sel & in1;

    // SICHER: Konsensterm hinzufügen, um Glitch zu eliminieren
    // Wenn in0 == in1, ist der Ausgang unabhängig vom sel-Glitch korrekt
    assign z = and_out1 | and_out2 | (in0 & in1);

    // Alternative: Ausgang registrieren
    // reg z_reg;
    // always @(posedge clk) z_reg <= and_out1 | and_out2;

endmodule

// SICHER: Sicherheitsentscheidung mit registrierten Eingängen
module secure_access_control (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        user_request,
    input  wire        admin_request,
    input  wire        is_admin,
    output reg         access_granted
);

    // SICHER: Alle Eingänge registrieren zur Synchronisation
    reg user_request_r, admin_request_r, is_admin_r;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            user_request_r <= 1'b0;
            admin_request_r <= 1'b0;
            is_admin_r <= 1'b0;
        end else begin
            // SICHER: Eingänge synchron abtasten
            user_request_r <= user_request;
            admin_request_r <= admin_request;
            is_admin_r <= is_admin;
        end
    end

    // SICHER: Sicherheitsentscheidung mit registrierten Werten treffen
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            access_granted <= 1'b0;
        end else begin
            // SICHER: Standardmäßig verweigern, explizit gewähren
            access_granted <= 1'b0;

            if (is_admin_r && admin_request_r) begin
                access_granted <= 1'b1;
            end else if (!is_admin_r && user_request_r) begin
                access_granted <= 1'b1;
            end
        end
    end

endmodule

// SICHER: DMA mit registrierten PMP-Signalen
module secure_dma_pmp (
    input  wire        clk_i,
    input  wire        rst_ni,
    input  wire [7:0][15:0] pmpcfg_i,
    input  wire [15:0][53:0] pmpaddr_i,
    input  wire [2:0]  dma_ctrl_reg,
    input  wire [31:0] dma_src_addr,
    input  wire [31:0] dma_dst_addr,
    output reg         dma_transfer_valid
);

    localparam CTRL_IDLE = 3'b000;
    localparam CTRL_SETUP = 3'b001;
    localparam CTRL_TRANSFER = 3'b010;
    localparam CTRL_DONE = 3'b011;

    // SICHER: PMP-Signale beim Start der Operation registrieren
    reg [7:0][15:0] pmpcfg_reg;
    reg [15:0][53:0] pmpaddr_reg;

    wire pmp_allow;

    // SICHER: PMP-Konfiguration nur im Idle/Done-Zustand erfassen
    always @(posedge clk_i or negedge rst_ni) begin
        if (!rst_ni) begin
            pmpcfg_reg <= '0;
            pmpaddr_reg <= '0;
        end else begin
            // SICHER: Nur aktualisieren, wenn keine aktive Übertragung läuft
            if (dma_ctrl_reg == CTRL_IDLE || dma_ctrl_reg == CTRL_DONE) begin
                pmpcfg_reg <= pmpcfg_i;
                pmpaddr_reg <= pmpaddr_i;
            end
            // SICHER: PMP-Werte während der Übertragung gesperrt
        end
    end

    // SICHER: Registrierte Werte für Sicherheitsprüfung verwenden
    pmp_checker u_pmp (
        .cfg(pmpcfg_reg),        // SICHER: Registrierter Wert
        .addr(pmpaddr_reg),      // SICHER: Kann sich während der Übertragung nicht ändern
        .check_addr(dma_src_addr),
        .allow(pmp_allow)
    );

    always @(posedge clk_i or negedge rst_ni) begin
        if (!rst_ni) begin
            dma_transfer_valid <= 1'b0;
        end else begin
            case (dma_ctrl_reg)
                CTRL_SETUP: begin
                    // SICHER: Prüfung verwendet stabile registrierte Werte
                    if (pmp_allow) begin
                        dma_transfer_valid <= 1'b1;
                    end else begin
                        dma_transfer_valid <= 1'b0;
                    end
                end

                CTRL_TRANSFER: begin
                    // SICHER: Übertragung verwendet gleiche Werte wie die Prüfung
                    // Angreifer kann PMP während der Übertragung nicht ändern
                end

                CTRL_DONE, CTRL_IDLE: begin
                    dma_transfer_valid <= 1'b0;
                end

                default: begin
                    dma_transfer_valid <= 1'b0;
                end
            endcase
        end
    end

endmodule

// SICHER: Sicherheits-FSM mit korrekter Synchronisation
module secure_security_fsm (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        auth_signal,
    input  wire        access_request,
    output reg         secure_access
);

    localparam STATE_LOCKED = 2'b00;
    localparam STATE_AUTH   = 2'b01;
    localparam STATE_OPEN   = 2'b10;

    reg [1:0] state;

    // SICHER: Eingänge registrieren
    reg auth_signal_r, access_request_r;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            auth_signal_r <= 1'b0;
            access_request_r <= 1'b0;
        end else begin
            auth_signal_r <= auth_signal;
            access_request_r <= access_request;
        end
    end

    // SICHER: Zustandsautomat mit registrierter Sicherheitsentscheidung
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= STATE_LOCKED;
            secure_access <= 1'b0;  // SICHER: Standardmäßig verweigern
        end else begin
            // SICHER: Standardmäßig jeden Takt verweigern
            secure_access <= 1'b0;

            case (state)
                STATE_LOCKED: begin
                    if (auth_signal_r) begin
                        state <= STATE_AUTH;
                    end
                end

                STATE_AUTH: begin
                    if (auth_signal_r && access_request_r) begin
                        state <= STATE_OPEN;
                    end else if (!auth_signal_r) begin
                        state <= STATE_LOCKED;
                    end
                end

                STATE_OPEN: begin
                    // SICHER: Zugriff nur im korrekten Zustand gewähren
                    secure_access <= 1'b1;

                    if (!auth_signal_r) begin
                        state <= STATE_LOCKED;
                        secure_access <= 1'b0;
                    end
                end

                default: begin
                    state <= STATE_LOCKED;
                end
            endcase
        end
    end

endmodule

CVE-Beispiele

  • CVE-2020-15802: Race Condition beim Bluetooth-Pairing ermöglichte Angreifern das Einschleusen von Schlüsseln.
  • CVE-2019-11091: Hardware-Race-Condition ermöglichte mikroarchitekturelles Daten-Sampling.

Verwandte CWEs

  • CWE-362: Nebenläufige Ausführung mit gemeinsamer Ressource bei unsachgemäßer Synchronisation (übergeordnet)
  • CWE-1199: Allgemeine Schaltungs- und Logikentwurfsprobleme (Kategorie)
  • CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1298: Hardware Logic Contains Race Conditions." https://cwe.mitre.org/data/definitions/1298.html
  2. CAPEC-26: Leveraging Race Conditions
  3. IEEE. "Digital Design Principles"