Gespiegelte Regionen mit unterschiedlichen Werten
Beschreibung
Gespiegelte Regionen mit unterschiedlichen Werten tritt auf, wenn die Architektur eines Produkts Regionen (wie Cache-Speicher oder Schattenkopien von Registern) dupliziert, ohne sicherzustellen, dass deren Inhalte stets synchron bleiben. Leistungsoptimierung erfordert oft die Duplizierung von Ressourcen, aber das Produkt muss sicherstellen, dass die lokale Kopie immer die Originalkopie wahrheitsgetreu widerspiegelt. Wenn die Synchronisation fehlschlägt, werden Berechnungsergebnisse unzuverlässig und Sicherheitsmechanismen können umgangen werden.
Risiko
Desynchronisierte gespiegelte Regionen haben schwerwiegende Sicherheitsauswirkungen. Zugriffskontrolle kann über veraltete Berechtigungen umgangen werden. Cache-Vergiftung kann zu Privilege Escalation führen. Schattenregister können fehlerhaften Sicherheitszustand enthalten. Speicherschutz kann umgangen werden. Kryptografische Schlüssel können inkonsistent sein. Der Authentifizierungszustand kann über Komponenten hinweg abweichen. Angreifer können Race Conditions während der Synchronisation ausnutzen. Sicherheitsentscheidungen können auf veralteten Daten basieren.
Lösung
Minimieren Sie Zeiträume der Desynchronisation und machen Sie den Aktualisierungsprozess so robust wie möglich. Stellen Sie sicher, dass Originalkopien Aktualisierungsbenachrichtigungen senden. Überprüfen Sie, dass Schattenkopien empfangene Aktualisierungen ausführen. Schützen Sie vor Race Conditions während Aktualisierungsfenstern. Authentifizieren Sie Aktualisierungsanfragen zur Verhinderung von Spoofing. Implementieren Sie Abschlussbestätigung zur Verhinderung von Rücksetzung. Fügen Sie Integritätsüberprüfung zwischen Original und Spiegel hinzu. Implementieren Sie atomare Aktualisierungsmechanismen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Umfang: Vertraulichkeit Unbefugter Datenzugriff - Veraltete gespiegelte Daten können Zugriff auf geschützte Ressourcen ermöglichen. |
| Integrität | Umfang: Integrität Datenbeschädigung - Inkonsistente Spiegel können fehlerhafte Berechnungen oder Sicherheitsentscheidungen verursachen. |
| Zugriffskontrolle | Umfang: Zugriffskontrolle Schutzmechanismus umgehen - Veraltete Berechtigungsdaten in Spiegeln können unbefugten Zugriff ermöglichen. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Register-Schattenkopie ohne Synchronisation
module vulnerable_register_mirror (
input wire clk,
input wire reset_n,
input wire [31:0] master_write_data,
input wire master_write_enable,
input wire [7:0] master_addr,
input wire shadow_read_enable,
input wire [7:0] shadow_read_addr,
output reg [31:0] shadow_read_data
);
// Master-Register
reg [31:0] master_regs [0:255];
// Schattenkopie für schnellen Zugriff
reg [31:0] shadow_regs [0:255];
// VERWUNDBAR: Master-Aktualisierung ohne Schatten-Synchronisation
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
integer i;
for (i = 0; i < 256; i = i + 1) begin
master_regs[i] <= 32'h0;
shadow_regs[i] <= 32'h0;
end
end
else if (master_write_enable) begin
// Master aktualisieren
master_regs[master_addr] <= master_write_data;
// VERWUNDBAR: Schatten-Aktualisierung ist asynchron
// Race Condition: Schatten wird möglicherweise nicht vor dem nächsten Lesen aktualisiert
end
end
// VERWUNDBAR: Separater Prozess für Schatten-Aktualisierung
// Kann Aktualisierungen verpassen oder desynchronisiert werden
always @(posedge clk) begin
// Hintergrund-Synchronisation - kann hinterherhinken
shadow_regs[0] <= master_regs[0];
shadow_regs[1] <= master_regs[1];
// ... synchronisiert nur wenige Register pro Zyklus
end
// Lesen aus möglicherweise veraltetem Schatten
always @(*) begin
if (shadow_read_enable) begin
shadow_read_data = shadow_regs[shadow_read_addr];
end
end
endmodule
// VERWUNDBAR: Cache ohne Kohärenz
module vulnerable_cache_mirror (
input wire clk,
input wire reset_n,
input wire [31:0] mem_addr,
input wire [31:0] mem_write_data,
input wire mem_write_enable,
input wire mem_read_enable,
output reg [31:0] mem_read_data,
output reg cache_hit
);
// Hauptspeicher
reg [31:0] main_memory [0:1023];
// Cache (Spiegel häufig zugegriffener Daten)
reg [31:0] cache_data [0:63];
reg [25:0] cache_tags [0:63];
reg cache_valid [0:63];
wire [5:0] cache_index = mem_addr[7:2];
wire [25:0] cache_tag = mem_addr[31:6];
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
// Initialisieren
integer i;
for (i = 0; i < 64; i = i + 1) begin
cache_valid[i] <= 1'b0;
end
end
else if (mem_write_enable) begin
// VERWUNDBAR: Write-Through ohne Invalidierungsbenachrichtigung
main_memory[mem_addr[11:2]] <= mem_write_data;
// Cache aktualisieren falls vorhanden
if (cache_valid[cache_index] && cache_tags[cache_index] == cache_tag) begin
cache_data[cache_index] <= mem_write_data;
end
// VERWUNDBAR: Andere Caches (bei Multiprozessor) nicht benachrichtigt
// Sie werden veraltete Daten haben
end
end
// Lesen mit möglicherweise veraltetem Cache
always @(*) begin
if (mem_read_enable) begin
if (cache_valid[cache_index] && cache_tags[cache_index] == cache_tag) begin
// VERWUNDBAR: Cache-Treffer aber Daten können veraltet sein
mem_read_data = cache_data[cache_index];
cache_hit = 1'b1;
end
else begin
mem_read_data = main_memory[mem_addr[11:2]];
cache_hit = 1'b0;
end
end
end
endmodule
// VERWUNDBAR: Speicherabgebildeter Register-Spiegel ohne Synchronisation
typedef struct {
volatile uint32_t* master_base;
uint32_t* shadow_copy;
size_t num_registers;
} VulnerableRegisterMirror;
void vulnerable_init(VulnerableRegisterMirror* mirror) {
// VERWUNDBAR: Einmalige Kopie bei Initialisierung
for (size_t i = 0; i < mirror->num_registers; i++) {
mirror->shadow_copy[i] = mirror->master_base[i];
}
// Schattenkopie wird nach jeder Master-Aktualisierung veraltet sein
}
uint32_t vulnerable_read(VulnerableRegisterMirror* mirror, size_t index) {
// VERWUNDBAR: Lesen aus Schatten ohne Aktualitätsprüfung
return mirror->shadow_copy[index];
}
void vulnerable_write(VulnerableRegisterMirror* mirror, size_t index, uint32_t value) {
// Zum Master schreiben
mirror->master_base[index] = value;
// VERWUNDBAR: Schatten-Aktualisierung kann unterbrochen werden
// Ein anderer Thread könnte veralteten Schatten zwischen diesen Zeilen lesen
mirror->shadow_copy[index] = value;
}
// VERWUNDBAR: DMA-Puffer-Spiegelung
typedef struct {
uint8_t* cpu_buffer;
uint8_t* dma_buffer;
size_t size;
} VulnerableDMAMirror;
void vulnerable_dma_prepare(VulnerableDMAMirror* mirror) {
// VERWUNDBAR: Kopie in DMA-Puffer ohne Synchronisation
memcpy(mirror->dma_buffer, mirror->cpu_buffer, mirror->size);
// DMA-Engine sieht möglicherweise teilweise aktualisierten Puffer
// Keine Speicherbarriere oder Cache-Flush
}
void vulnerable_dma_complete(VulnerableDMAMirror* mirror) {
// VERWUNDBAR: Rückkopie ohne Synchronisation
memcpy(mirror->cpu_buffer, mirror->dma_buffer, mirror->size);
// CPU-Cache hat möglicherweise noch alte Daten
// Keine Cache-Invalidierung
}
Sichere Lösung
// SICHER: Register-Schatten mit verifizierter Synchronisation
module secure_register_mirror (
input wire clk,
input wire reset_n,
input wire [31:0] master_write_data,
input wire master_write_enable,
input wire [7:0] master_addr,
input wire shadow_read_enable,
input wire [7:0] shadow_read_addr,
output reg [31:0] shadow_read_data,
output reg sync_valid,
output reg sync_error
);
// Master-Register
reg [31:0] master_regs [0:255];
// Schattenkopie mit Versionsverfolgung
reg [31:0] shadow_regs [0:255];
reg [15:0] master_version [0:255];
reg [15:0] shadow_version [0:255];
// SICHER: Atomare Aktualisierung von Master und Schatten
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
integer i;
for (i = 0; i < 256; i = i + 1) begin
master_regs[i] <= 32'h0;
shadow_regs[i] <= 32'h0;
master_version[i] <= 16'h0;
shadow_version[i] <= 16'h0;
end
sync_valid <= 1'b1;
sync_error <= 1'b0;
end
else if (master_write_enable) begin
// SICHER: Master und Schatten atomar aktualisieren
master_regs[master_addr] <= master_write_data;
shadow_regs[master_addr] <= master_write_data;
// Versionen gemeinsam inkrementieren
master_version[master_addr] <= master_version[master_addr] + 1;
shadow_version[master_addr] <= master_version[master_addr] + 1;
end
end
// SICHER: Synchronisation vor Datenrückgabe überprüfen
always @(*) begin
if (shadow_read_enable) begin
// Versionsübereinstimmung prüfen
if (shadow_version[shadow_read_addr] == master_version[shadow_read_addr]) begin
shadow_read_data = shadow_regs[shadow_read_addr];
sync_valid = 1'b1;
end
else begin
// SICHER: Ausfallsicher bei Nichtübereinstimmung
shadow_read_data = 32'h0;
sync_valid = 1'b0;
sync_error = 1'b1;
end
end
end
// SICHER: Periodische Integritätsprüfung
reg [7:0] check_index;
always @(posedge clk) begin
if (master_regs[check_index] != shadow_regs[check_index]) begin
sync_error <= 1'b1;
end
check_index <= check_index + 1;
end
endmodule
// SICHER: Cache mit Kohärenzprotokoll
module secure_cache_coherent (
input wire clk,
input wire reset_n,
input wire [31:0] mem_addr,
input wire [31:0] mem_write_data,
input wire mem_write_enable,
input wire mem_read_enable,
input wire [3:0] core_id,
input wire invalidate_req,
input wire [31:0] invalidate_addr,
output reg [31:0] mem_read_data,
output reg cache_hit,
output reg invalidate_ack
);
// Cache-Zustand (MESI-Protokoll)
parameter INVALID = 2'b00;
parameter SHARED = 2'b01;
parameter EXCLUSIVE = 2'b10;
parameter MODIFIED = 2'b11;
reg [31:0] cache_data [0:63];
reg [25:0] cache_tags [0:63];
reg [1:0] cache_state [0:63];
wire [5:0] cache_index = mem_addr[7:2];
wire [25:0] cache_tag = mem_addr[31:6];
wire [5:0] inv_index = invalidate_addr[7:2];
wire [25:0] inv_tag = invalidate_addr[31:6];
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
integer i;
for (i = 0; i < 64; i = i + 1) begin
cache_state[i] <= INVALID;
end
invalidate_ack <= 1'b0;
end
// SICHER: Invalidierungsanfragen von anderen Caches behandeln
else if (invalidate_req) begin
if (cache_tags[inv_index] == inv_tag &&
cache_state[inv_index] != INVALID) begin
// Zurückschreiben falls modifiziert
if (cache_state[inv_index] == MODIFIED) begin
// Zurückschreiben in Speicher auslösen vor Invalidierung
writeback_to_memory(inv_index);
end
cache_state[inv_index] <= INVALID;
end
invalidate_ack <= 1'b1;
end
else if (mem_write_enable) begin
// SICHER: Aktualisierung mit korrektem Zustandsübergang
cache_data[cache_index] <= mem_write_data;
cache_tags[cache_index] <= cache_tag;
cache_state[cache_index] <= MODIFIED;
// SICHER: Invalidierung an andere Caches senden
// (wird durch Verbindungsstruktur behandelt)
end
else begin
invalidate_ack <= 1'b0;
end
end
always @(*) begin
if (mem_read_enable) begin
if (cache_tags[cache_index] == cache_tag &&
cache_state[cache_index] != INVALID) begin
mem_read_data = cache_data[cache_index];
cache_hit = 1'b1;
end
else begin
// Cache-Fehlschlag - aus Speicher holen
mem_read_data = fetch_from_memory(mem_addr);
cache_hit = 1'b0;
end
end
end
endmodule
// SICHER: Speicherabgebildeter Register-Spiegel mit Synchronisation
#include <stdint.h>
#include <stdatomic.h>
#include <string.h>
typedef struct {
volatile uint32_t* master_base;
uint32_t* shadow_copy;
atomic_uint* versions;
size_t num_registers;
pthread_spinlock_t lock;
} SecureRegisterMirror;
int secure_init(SecureRegisterMirror* mirror) {
pthread_spin_init(&mirror->lock, PTHREAD_PROCESS_PRIVATE);
// SICHER: Atomare Initialisierung mit Versionsverfolgung
pthread_spin_lock(&mirror->lock);
for (size_t i = 0; i < mirror->num_registers; i++) {
mirror->shadow_copy[i] = mirror->master_base[i];
atomic_store(&mirror->versions[i], 1);
}
pthread_spin_unlock(&mirror->lock);
return 0;
}
uint32_t secure_read(SecureRegisterMirror* mirror, size_t index) {
uint32_t value;
unsigned int version1, version2;
// SICHER: Seqlock-Muster für konsistentes Lesen
do {
version1 = atomic_load(&mirror->versions[index]);
// Schatten lesen
value = mirror->shadow_copy[index];
// Speicherbarriere
atomic_thread_fence(memory_order_acquire);
version2 = atomic_load(&mirror->versions[index]);
// Wiederholen falls Version sich während des Lesens geändert hat
} while (version1 != version2 || (version1 & 1));
// SICHER: Periodisch gegen Master verifizieren
if ((version1 % 100) == 0) {
uint32_t master_value = mirror->master_base[index];
if (value != master_value) {
// Resynchronisieren und Fehler protokollieren
secure_resync(mirror, index);
return mirror->shadow_copy[index];
}
}
return value;
}
void secure_write(SecureRegisterMirror* mirror, size_t index, uint32_t value) {
pthread_spin_lock(&mirror->lock);
// SICHER: Version als ungerade markieren (Schreiben im Gange)
unsigned int old_version = atomic_fetch_add(&mirror->versions[index], 1);
// Speicherbarriere
atomic_thread_fence(memory_order_release);
// Atomar in beide schreiben
mirror->master_base[index] = value;
mirror->shadow_copy[index] = value;
// Speicherbarriere
atomic_thread_fence(memory_order_release);
// Version als gerade markieren (Schreiben abgeschlossen)
atomic_fetch_add(&mirror->versions[index], 1);
pthread_spin_unlock(&mirror->lock);
}
// SICHER: DMA-Puffer-Spiegelung mit korrekter Synchronisation
typedef struct {
uint8_t* cpu_buffer;
uint8_t* dma_buffer;
size_t size;
volatile int sync_state;
} SecureDMAMirror;
void secure_dma_prepare(SecureDMAMirror* mirror) {
// SICHER: CPU-Cache zuerst leeren
cache_flush(mirror->cpu_buffer, mirror->size);
// Speicherbarriere
__sync_synchronize();
// In DMA-Puffer kopieren
memcpy(mirror->dma_buffer, mirror->cpu_buffer, mirror->size);
// SICHER: Speicherbarriere vor DMA-Start
__sync_synchronize();
mirror->sync_state = 1; // DMA bereit
}
void secure_dma_complete(SecureDMAMirror* mirror) {
// SICHER: Auf DMA-Abschluss warten
while (!dma_is_complete()) {
// Aktives Warten oder Yielden
}
// SICHER: CPU-Cache invalidieren
cache_invalidate(mirror->cpu_buffer, mirror->size);
// Speicherbarriere
__sync_synchronize();
// Zurückkopieren
memcpy(mirror->cpu_buffer, mirror->dma_buffer, mirror->size);
// Speicherbarriere nach Aktualisierung
__sync_synchronize();
mirror->sync_state = 2; // CPU bereit
}
CVE-Beispiele
Schwachstellen in gespiegelten Regionen wurden in Prozessor-Cache-Implementierungen, TLB-Einträgen und Schatten-Registerbänken gefunden, bei denen Angreifer Synchronisationslücken ausnutzten, um auf geschützte Daten zuzugreifen oder Zugriffskontrollen zu umgehen.
Verwandte CWEs
- CWE-1250: Unsachgemäße Erhaltung der Konsistenz zwischen unabhängigen Darstellungen gemeinsamen Zustands (übergeordnet)
- CWE-1312: Fehlender Schutz für gespiegelte Regionen in On-Chip-Fabric-Firewall (verwandt)
- CWE-362: Gleichzeitige Ausführung mit gemeinsamer Ressource und unsachgemäßer Synchronisation (verwandt)
Referenzen
- MITRE Corporation. "CWE-1251: Mirrored Regions with Different Values." https://cwe.mitre.org/data/definitions/1251.html
- Patterson & Hennessy. "Computer Organization and Design" - Cache Coherence Protocols
- MESI Protocol Documentation