Sequenz von Prozessorinstruktionen führt zu unerwartetem Verhalten
Beschreibung
Sequenz von Prozessorinstruktionen führt zu unerwartetem Verhalten tritt auf, wenn bestimmte Kombinationen von Prozessorinstruktionen zu unerwünschtem Verhalten führen, wie z.B. das Blockieren des Prozessors bis ein Hard-Reset durchgeführt wird. Problematische Instruktionssequenzen können aus Lücken im ISA-Design (Instruction Set Architecture) oder der Prozessorimplementierung entstehen. Während illegale Instruktions-Opcodes Ausnahmen ohne Sicherheitsauswirkungen auslösen sollten, können bestimmte Instruktionskombinationen zu Prozessor-Lockup oder anderen schädlichen Effekten führen und möglicherweise unprivilegiertem Code ermöglichen, die CPU vollständig einzufrieren.
Risiko
Problematische Instruktionssequenzen haben schwerwiegende Sicherheitsauswirkungen. Prozessor-Lockup kann auftreten. Denial of Service ist möglich. Systemverfügbarkeit wird beeinträchtigt. Unprivilegierter Code kann das System abstürzen lassen. Hard-Reset kann erforderlich sein. Kritische Operationen können unterbrochen werden. Sicherheitssysteme können beeinträchtigt werden. Eingebettete Systeme können ausfallen.
Lösung
Implementieren Sie eine rigorose Teststrategie, die Randomisierung einbezieht, um Instruktionssequenzen zu erkunden, die in normalen Workloads unwahrscheinlich auftreten. Aktualisieren Sie Betriebssysteme, um die Ausführung problematischer Sequenzen zu verhindern oder resultierende Schäden zu mindern. Verwenden Sie Microcode-Patches wo möglich. Implementieren Sie Watchdog-Timer zur Wiederherstellung nach Hängern. Erwägen Sie Instruktionsfilterung in sensiblen Kontexten.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Scope: Verfügbarkeit DoS: Prozessor kann blockieren und erfordert Hard-Reset. |
| Integrität | Scope: Integrität Systembetrieb wird durch Instruktionssequenzen gestört. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Prozessor mit problematischer Instruktionsbehandlung
module vulnerable_processor (
input wire clk,
input wire reset_n,
input wire [31:0] instruction,
input wire instruction_valid,
output reg [31:0] result,
output reg instruction_complete,
output reg exception
);
// Instruktionsdekodierung
wire [5:0] opcode = instruction[31:26];
wire [4:0] rs = instruction[25:21];
wire [4:0] rt = instruction[20:16];
// Prozessorzustand
reg [31:0] registers [0:31];
reg [31:0] pc;
reg locked;
reg in_atomic;
// Atomarer Operationszustand
reg atomic_lock_held;
reg [31:0] atomic_address;
// VERWUNDBAR: Problematische Instruktionssequenzen
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
locked <= 1'b0;
in_atomic <= 1'b0;
instruction_complete <= 1'b0;
end
else if (instruction_valid && !locked) begin
case (opcode)
// VERWUNDBAR: LOCK-Präfix mit CMPXCHG auf Register
// (Ähnlich dem Pentium F00F Bug)
6'b110000: begin // LOCK
atomic_lock_held <= 1'b1;
end
6'b110001: begin // CMPXCHG8B
if (atomic_lock_held && rs == 5'b00000) begin
// VERWUNDBAR: LOCK CMPXCHG8B auf Register
// Ausnahme sollte ausgelöst werden, aber Lock wird nie freigegeben
exception <= 1'b1;
// VERWUNDBAR: atomic_lock_held bleibt gesetzt!
// Bus-Lock wird nie freigegeben - System-Deadlock
locked <= 1'b1; // Prozessor hängt
end
end
// VERWUNDBAR: Aufeinanderfolgende XCHG auf gleichem Register
// (Ähnlich dem Cyrix Coma Bug)
6'b100000: begin // XCHG
// XCHG sperrt Bus, aber aufeinanderfolgende XCHG verhindert
// Interrupt-Behandlung
if (in_atomic && previous_opcode == 6'b100000) begin
// VERWUNDBAR: Endlosschleife blockiert Interrupts
// Prozessor kann auf keine Signale reagieren
locked <= 1'b1;
end
in_atomic <= 1'b1;
end
default: begin
in_atomic <= 1'b0;
atomic_lock_held <= 1'b0;
end
endcase
end
end
endmodule
// VERWUNDBAR: Atomare Instruktion ohne ordnungsgemäße Prüfungen
module vulnerable_atomic_handler (
input wire clk,
input wire reset_n,
input wire [31:0] instruction,
input wire execute,
input wire interrupt_pending,
output reg atomic_active,
output reg interrupt_blocked,
output reg stuck
);
wire is_atomic = instruction[31]; // Atomares Flag
// VERWUNDBAR: Kein Limit für atomare Sequenzlänge
reg [15:0] atomic_count;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_count <= 16'h0;
stuck <= 1'b0;
end
else if (execute) begin
if (is_atomic) begin
atomic_active <= 1'b1;
interrupt_blocked <= 1'b1;
atomic_count <= atomic_count + 1;
// VERWUNDBAR: Keine maximale atomare Sequenz
// Bösartiger Code kann Interrupts für immer blockieren
// durch Ausführung kontinuierlicher atomarer Instruktionen
end
else begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_count <= 16'h0;
end
// System-Watchdog würde schließlich auslösen, aber
// kritische Echtzeit-Deadlines könnten verpasst werden
end
end
endmodule
// VERWUNDBAR: Software, die Prozessor-Bugs auslösen kann
#include <stdint.h>
// VERWUNDBAR: Pentium F00F Bug-Auslöser
// Unprivilegierter Code kann Prozessor sperren
void vulnerable_f00f_trigger(void) {
// VERWUNDBAR: Diese Sequenz sperrt den Prozessor
// Kein Betriebssystemschutz kann dies verhindern
__asm__ volatile (
"lock cmpxchg8b (%eax)\n" // F00F Bug!
);
// Prozessor ist nun gesperrt
// Nur Hard-Reset kann wiederherstellen
// Denial of Service erreicht
}
// VERWUNDBAR: Cyrix Coma Bug-Auslöser
void vulnerable_coma_trigger(void) {
// VERWUNDBAR: Aufeinanderfolgende XCHG verhindert Interrupt-Behandlung
volatile int x = 0;
while (1) {
__asm__ volatile (
"xchg %%eax, %0\n"
"xchg %%eax, %0\n"
: "+m"(x)
:
: "eax"
);
}
// Interrupts werden für immer blockiert
// System erscheint eingefroren
}
// VERWUNDBAR: Ausnutzung undefinierter Opcodes
void vulnerable_undefined_opcode(void) {
// Einige Prozessoren behandeln nicht alle undefinierten Opcodes korrekt
// Dies könnte unvorhersehbares Verhalten verursachen
uint8_t undefined_sequence[] = {0x0F, 0xFF, 0xFF, 0xFF};
// Undefinierte Instruktion ausführen
void (*func)(void) = (void (*)(void))undefined_sequence;
func(); // Kann abstürzen, hängen oder Sicherheitsprobleme verursachen
}
Sichere Lösung
// SICHER: Prozessor mit sicherer Instruktionsbehandlung
module secure_processor (
input wire clk,
input wire reset_n,
input wire [31:0] instruction,
input wire instruction_valid,
output reg [31:0] result,
output reg instruction_complete,
output reg exception,
input wire nmi, // Non-maskable Interrupt
output reg processor_healthy
);
wire [5:0] opcode = instruction[31:26];
wire [4:0] rs = instruction[25:21];
reg [31:0] registers [0:31];
reg atomic_lock_held;
reg [15:0] atomic_timeout;
// SICHER: Watchdog für Instruktionsabschluss
reg [15:0] instruction_timeout;
parameter MAX_INSTRUCTION_CYCLES = 16'd1000;
// SICHER: Maximale atomare Sequenzlänge
parameter MAX_ATOMIC_CYCLES = 16'd256;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
atomic_lock_held <= 1'b0;
atomic_timeout <= 16'h0;
instruction_timeout <= 16'h0;
processor_healthy <= 1'b1;
exception <= 1'b0;
end
else begin
// SICHER: Instruktions-Timeout-Watchdog
if (instruction_valid && !instruction_complete) begin
instruction_timeout <= instruction_timeout + 1;
if (instruction_timeout >= MAX_INSTRUCTION_CYCLES) begin
// SICHER: Instruktionsabbruch erzwingen
exception <= 1'b1;
instruction_complete <= 1'b1;
instruction_timeout <= 16'h0;
end
end
else begin
instruction_timeout <= 16'h0;
end
// SICHER: Atomarer Timeout
if (atomic_lock_held) begin
atomic_timeout <= atomic_timeout + 1;
if (atomic_timeout >= MAX_ATOMIC_CYCLES) begin
// SICHER: Freigabe des atomaren Locks erzwingen
atomic_lock_held <= 1'b0;
exception <= 1'b1;
atomic_timeout <= 16'h0;
end
end
// SICHER: NMI wird immer berücksichtigt
if (nmi) begin
atomic_lock_held <= 1'b0;
// NMI behandeln
end
if (instruction_valid) begin
case (opcode)
6'b110000: begin // LOCK-Präfix
atomic_lock_held <= 1'b1;
atomic_timeout <= 16'h0;
end
6'b110001: begin // CMPXCHG8B
if (atomic_lock_held && rs == 5'b00000) begin
// SICHER: Ordnungsgemäße Behandlung von ungültigem LOCK CMPXCHG8B
// Lock VOR Auslösen der Ausnahme freigeben
atomic_lock_held <= 1'b0;
exception <= 1'b1;
instruction_complete <= 1'b1;
end
end
6'b100000: begin // XCHG
// SICHER: Aufeinanderfolgende Atomics begrenzen
// Wird durch atomic_timeout oben behandelt
end
default: begin
atomic_lock_held <= 1'b0;
atomic_timeout <= 16'h0;
end
endcase
end
end
end
endmodule
// SICHER: Atomarer Handler mit Limits
module secure_atomic_handler (
input wire clk,
input wire reset_n,
input wire [31:0] instruction,
input wire execute,
input wire interrupt_pending,
input wire nmi,
output reg atomic_active,
output reg interrupt_blocked,
output reg atomic_violation
);
wire is_atomic = instruction[31];
// SICHER: Limit für atomare Operationen
reg [7:0] atomic_count;
parameter MAX_ATOMIC_OPS = 8'd64;
// SICHER: Timer für atomares Fenster
reg [15:0] atomic_timer;
parameter MAX_ATOMIC_TIME = 16'd1000;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_count <= 8'h0;
atomic_timer <= 16'h0;
atomic_violation <= 1'b0;
end
else begin
atomic_violation <= 1'b0;
// SICHER: NMI unterbricht immer atomare Sequenz
if (nmi) begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_count <= 8'h0;
atomic_timer <= 16'h0;
end
else if (execute) begin
if (is_atomic) begin
atomic_count <= atomic_count + 1;
atomic_timer <= atomic_timer + 1;
// SICHER: Limits prüfen
if (atomic_count >= MAX_ATOMIC_OPS ||
atomic_timer >= MAX_ATOMIC_TIME) begin
// SICHER: Ende der atomaren Sequenz erzwingen
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_violation <= 1'b1; // Verletzung signalisieren
end
else begin
atomic_active <= 1'b1;
interrupt_blocked <= 1'b1;
end
end
else begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_count <= 8'h0;
atomic_timer <= 16'h0;
end
end
else if (atomic_active) begin
// Zeit auch ohne neue Instruktionen zählen
atomic_timer <= atomic_timer + 1;
if (atomic_timer >= MAX_ATOMIC_TIME) begin
atomic_active <= 1'b0;
interrupt_blocked <= 1'b0;
atomic_violation <= 1'b1;
end
end
end
end
endmodule
// SICHER: OS-Level-Schutz gegen problematische Sequenzen
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
// SICHER: OS-Emulation/Abfangen problematischer Instruktionen
typedef struct {
uint8_t* opcode_bytes;
size_t length;
const char* name;
} problematic_sequence_t;
// Bekannte problematische Sequenzen
static const problematic_sequence_t bad_sequences[] = {
{{0xF0, 0x0F, 0xC7, 0xC8}, 4, "F00F bug"}, // LOCK CMPXCHG8B EAX
{{0x0F, 0xFF}, 2, "Undefined 0F FF"},
// Weitere bekannte schlechte Sequenzen hinzufügen
};
// SICHER: Instruktionsemulator für problematische Sequenzen
bool check_and_emulate_instruction(uint8_t* instruction, size_t len,
cpu_context_t* ctx) {
// Gegen bekannte schlechte Sequenzen prüfen
for (size_t i = 0; i < sizeof(bad_sequences)/sizeof(bad_sequences[0]); i++) {
if (len >= bad_sequences[i].length &&
memcmp(instruction, bad_sequences[i].opcode_bytes,
bad_sequences[i].length) == 0) {
log_security_event("Blöcked problematic instruction: %s",
bad_sequences[i].name);
// SICHER: Sicheres Verhalten emulieren statt auszuführen
ctx->eflags |= FLAG_INVALID_OPCODE;
return true; // Behandelt
}
}
return false; // Keine bekannte schlechte Sequenz
}
// SICHER: Kernel-Watchdog für hängende CPUs
void setup_cpu_watchdog(void) {
// Hardware-Watchdog konfigurieren
configure_watchdog_timer(WATCHDOG_TIMEOUT_MS);
// NMI-Handler für Watchdog-Ablauf registrieren
register_nmi_handler(watchdog_nmi_handler);
enable_watchdog();
}
void watchdog_nmi_handler(void) {
// CPU könnte hängen - Wiederherstellung versuchen
// Problem protokollieren
log_emergency("CPU watchdog triggered - possible hang");
// Hängende Instruktion ermitteln
uint32_t stuck_pc = get_current_pc();
log_emergency("Stuck at PC: 0x%08x", stuck_pc);
// SICHER: CPU-Zustand-Reset erzwingen wenn möglich
if (is_in_atomic()) {
force_release_atomic_lock();
}
// SICHER: Wiederherstellung versuchen
if (!recover_cpu_state()) {
// Kann nicht wiederherstellen - kontrolliertes Herunterfahren
emergency_shutdown();
}
}
// SICHER: User-Space-Schutz
void setup_instruction_filter(void) {
// Für sensible Umgebungen, Instruktionen in VM/Sandbox filtern
// In Instruktionsdekoder einhaken
set_instruction_callback(instruction_filter_callback);
}
bool instruction_filter_callback(uint8_t* instruction, size_t len) {
// Auf gefährliche Muster prüfen
if (is_lock_prefix(instruction[0])) {
// Verifizieren, dass LOCK sicher verwendet wird
if (!is_safe_lock_usage(instruction, len)) {
inject_exception(EXCEPTION_INVALID_OPCODE);
return false; // Nicht ausführen
}
}
// Auf Endlosschleifen-Muster prüfen
if (is_tight_atomic_loop(instruction, len)) {
inject_exception(EXCEPTION_INVALID_OPCODE);
return false;
}
return true; // OK zum Ausführen
}
CVE-Beispiele
- CVE-2021-26339: AMD-CPU-Instruktionssequenz verursacht Prozessor-Hang.
- CVE-1999-1476: Intel Pentium CMPXCHG8B (F00F Bug) Deadlock-Schwachstelle - die "lock cmpxchg8b eax"-Instruktion verursacht Prozessor-Sperre, da die gesperrte Speichertransaktion nie abgeschlossen wird.
Verwandte CWEs
- CWE-691: Insufficient Control Flow Management (übergeordnet)
- CWE-1201: Core and Compute Issues (Kategorie)
- CWE-400: Uncontrolled Resource Consumption (verwandt)
- CAPEC-212: Functionality Misuse (Angriffsmuster)
Referenzen
- MITRE Corporation. "CWE-1281: Sequence of Processor Instructions Leads to Unexpected Behavior." https://cwe.mitre.org/data/definitions/1281.html
- Intel. "Pentium Processor Specification Update"
- AMD. "AMD Processor Security Updates"