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
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismen werden während der Race-Condition-Fenster umgangen. |
| Autorisierung | Bereich: Autorisierung Privilege Escalation oder Identitätsübernahme durch Timing-Angriffe. |
| Integrität | Bereich: 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
- MITRE Corporation. "CWE-1298: Hardware Logic Contains Race Conditions." https://cwe.mitre.org/data/definitions/1298.html
- CAPEC-26: Leveraging Race Conditions
- IEEE. "Digital Design Principles"