Unsachgemäße Schreibbehandlung in nichtflüchtigen Speichern mit begrenzter Schreibkapazität
Beschreibung
Unsachgemäße Schreibbehandlung in nichtflüchtigen Speichern mit begrenzter Schreibkapazität tritt auf, wenn ein Produkt Wear-Leveling-Operationen in nichtflüchtigen Speichern mit begrenzten Programmier-/Löschzyklen nicht oder fehlerhaft implementiert. Nichtflüchtige Speichertechnologien wie NAND Flash und EEPROM haben individuell löschbare Segmente mit begrenzten Programmier-/Löschzyklen. Wear Leveling bildet logische Blockschreibvorgänge auf verschiedene physische Blöcke ab, um vorzeitiges Versagen durch konzentrierte Schreibvorgänge zu verhindern. Eine unsachgemäße Implementierung dieser Technik schafft Schwachstellen, die Angreifer ausnutzen können, um Denial of Service durch beschleunigte Speicherdegradation zu verursachen.
Risiko
Unsachgemäßes Wear Leveling hat erhebliche Verfügbarkeitsauswirkungen. Speicher kann vorzeitig unzuverlässig werden. Daten können aufgrund abgenutzter Blöcke verloren gehen. Denial of Service kann durch Schreibangriffe erreicht werden. Die Systemlebensdauer kann erheblich verkürzt werden. Kritischer Firmware-Speicher kann ausfallen. Sicherheitskritische Protokolle können verloren gehen. Die Boot-Fähigkeit kann beeinträchtigt werden. Wiederherstellungsoptionen können eliminiert werden.
Lösung
Integrieren Sie sichere Wear-Leveling-Algorithmen und stellen Sie sicher, dass sie nicht umgangen werden können. Implementieren Sie Schreibratenbegrenzung zur Vermeidung beschleunigten Verschleißes. Überwachen Sie Block-Verschleißgrade und alarmieren Sie bei abnormalen Mustern. Reservieren Sie Ersatzblöcke für Wear Leveling. Implementieren Sie Bad-Block-Management. Testen Sie die Wirksamkeit des Wear Leveling. Dokumentieren Sie die erwartete Schreibdauerhaftigkeit. Erwägen Sie Speichertechnologien mit höherer Dauerhaftigkeit für kritische Daten.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit DoS: Instabilität - Angreifer können programmatisch dafür sorgen, dass Speicher durch unsachgemäße Wear-Leveling-Implementierung oder Umgehung des Wear Leveling deutlich schneller als erwartet unzuverlässig wird. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Keine Wear-Leveling-Implementierung
#include <stdint.h>
#define FLASH_BLOCK_COUNT 1024
#define FLASH_BLOCK_SIZE 4096
#define MAX_ERASE_CYCLES 100000
// Direkte Zuordnung - kein Wear Leveling
uint32_t logical_to_physical[FLASH_BLOCK_COUNT];
void vulnerable_init(void) {
// VERWUNDBAR: Direkte 1:1-Zuordnung
for (int i = 0; i < FLASH_BLOCK_COUNT; i++) {
logical_to_physical[i] = i;
}
}
void vulnerable_write_block(uint32_t logical_block, uint8_t* data) {
// VERWUNDBAR: Schreibt immer auf denselben physischen Block
uint32_t physical_block = logical_to_physical[logical_block];
// Kein Wear Leveling - dieser Block wird zuerst verschleißen
flash_erase_block(physical_block);
flash_write_block(physical_block, data);
}
// Angriff: Wiederholt auf denselben logischen Block schreiben
void wear_attack(void) {
uint8_t data[FLASH_BLOCK_SIZE];
// ANGRIFF: Auf denselben Block schreiben, bis er ausfällt
while (1) {
for (int i = 0; i < MAX_ERASE_CYCLES + 1; i++) {
// Block 0 wird nach ~100.000 Schreibvorgängen ausfallen
vulnerable_write_block(0, data);
}
// Systemspeicher jetzt unzuverlässig!
}
}
// VERWUNDBAR: Einfacher Zähler ohne Wear Leveling
class VulnerableCounter {
private:
uint32_t counter_address;
public:
void increment(void) {
uint32_t value = flash_read(counter_address);
// VERWUNDBAR: Schreibt immer an dieselbe Stelle
flash_erase(counter_address);
flash_write(counter_address, value + 1);
// Zählerposition wird schnell verschleißen
}
};
// VERWUNDBAR: Flash-Controller ohne Wear Leveling
module vulnerable_flash_controller (
input wire clk,
input wire reset_n,
input wire [31:0] logical_address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
output reg [31:0] read_data,
output reg operation_complete
);
// VERWUNDBAR: Direkte Adresszuordnung
// Keine Wear-Leveling-Logik
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
operation_complete <= 1'b0;
end
else if (write_enable) begin
// VERWUNDBAR: Direkt an physische Adresse schreiben
// Gleiche logische Adresse = gleiche physische Adresse = beschleunigter Verschleiß
flash_write(logical_address, write_data);
operation_complete <= 1'b1;
end
else if (read_enable) begin
read_data <= flash_read(logical_address);
operation_complete <= 1'b1;
end
end
endmodule
Sichere Lösung
// SICHER: Wear-Leveling-Implementierung
#include <stdint.h>
#include <algorithm>
#define FLASH_BLOCK_COUNT 1024
#define FLASH_BLOCK_SIZE 4096
#define MAX_ERASE_CYCLES 100000
#define WEAR_THRESHOLD 1000 // Leveling auslösen, wenn Differenz diesen Wert überschreitet
class SecureFlashManager {
private:
struct BlockInfo {
uint32_t logical_block;
uint32_t erase_count;
bool valid;
bool bad;
};
BlockInfo block_table[FLASH_BLOCK_COUNT];
uint32_t logical_to_physical[FLASH_BLOCK_COUNT];
uint32_t spare_blocks[64]; // Reserveblöcke für Wear Leveling
int spare_count;
public:
void init(void) {
// Verschleißüberwachung initialisieren
for (int i = 0; i < FLASH_BLOCK_COUNT - 64; i++) {
block_table[i].logical_block = i;
block_table[i].erase_count = read_erase_count(i);
block_table[i].valid = true;
block_table[i].bad = is_bad_block(i);
logical_to_physical[i] = i;
}
// Reservepool initialisieren
spare_count = 0;
for (int i = FLASH_BLOCK_COUNT - 64; i < FLASH_BLOCK_COUNT; i++) {
if (!is_bad_block(i)) {
spare_blocks[spare_count++] = i;
}
}
}
bool write_block(uint32_t logical_block, uint8_t* data) {
if (logical_block >= FLASH_BLOCK_COUNT - 64) {
return false; // Ungültiger Block
}
uint32_t physical_block = logical_to_physical[logical_block];
// Prüfen, ob Wear Leveling durchgeführt werden sollte
if (should_level_wear(physical_block)) {
physical_block = perform_wear_leveling(logical_block);
if (physical_block == INVALID_BLOCK) {
return false; // Keine Reserveblöcke verfügbar
}
}
// Löschen und schreiben
if (!flash_erase_block(physical_block)) {
mark_bad_block(physical_block);
return write_block(logical_block, data); // Mit neuem Block wiederholen
}
block_table[physical_block].erase_count++;
save_erase_count(physical_block);
return flash_write_block(physical_block, data);
}
private:
bool should_level_wear(uint32_t physical_block) {
uint32_t min_wear = get_min_erase_count();
uint32_t block_wear = block_table[physical_block].erase_count;
// Leveling, wenn dieser Block deutlich stärker abgenutzt ist
return (block_wear - min_wear) > WEAR_THRESHOLD;
}
uint32_t perform_wear_leveling(uint32_t logical_block) {
// Am wenigsten abgenutzten Reserveblock finden
uint32_t new_physical = find_least_worn_spare();
if (new_physical == INVALID_BLOCK) {
// Keine Reserve - am wenigsten abgenutzten verwendeten Block finden
new_physical = find_least_worn_block();
if (new_physical == INVALID_BLOCK) {
return INVALID_BLOCK;
}
// Daten von diesem Block an alten Ort verschieben
swap_blocks(new_physical, logical_to_physical[logical_block]);
}
// Zuordnung aktualisieren
uint32_t old_physical = logical_to_physical[logical_block];
logical_to_physical[logical_block] = new_physical;
// Alter Block wird Reserve
add_to_spare_pool(old_physical);
return new_physical;
}
uint32_t get_min_erase_count(void) {
uint32_t min = UINT32_MAX;
for (int i = 0; i < FLASH_BLOCK_COUNT; i++) {
if (!block_table[i].bad && block_table[i].erase_count < min) {
min = block_table[i].erase_count;
}
}
return min;
}
};
// SICHER: Sicherer Zähler mit Verschleißverteilung
class SecureCounter {
private:
SecureFlashManager* flash_mgr;
uint32_t counter_blocks[16]; // Mehrere Blöcke für Zähler
int current_block_index;
public:
uint64_t read_counter(void) {
// Zähler über mehrere Blöcke gespeichert
uint64_t value = 0;
for (int i = 0; i < 16; i++) {
value += flash_mgr->read_block(counter_blocks[i]);
}
return value;
}
void increment(void) {
// SICHER: Über mehrere Blöcke rotieren
current_block_index = (current_block_index + 1) % 16;
uint8_t data[FLASH_BLOCK_SIZE] = {0};
data[0] = 1; // Inkrementierungsmarkierung
flash_mgr->write_block(counter_blocks[current_block_index], data);
}
};
// SICHER: Flash-Controller mit Wear Leveling
module secure_flash_controller (
input wire clk,
input wire reset_n,
input wire [31:0] logical_address,
input wire [31:0] write_data,
input wire write_enable,
input wire read_enable,
output reg [31:0] read_data,
output reg operation_complete,
output reg wear_warning
);
// Wear-Leveling-Tabelle
reg [31:0] logical_to_physical [0:1023];
reg [23:0] erase_count [0:1023];
// Schwellenwert für Wear Leveling
parameter WEAR_THRESHOLD = 24'd1000;
// Wear-Leveling-Zustandsautomat
reg [2:0] wl_state;
parameter WL_IDLE = 3'h0;
parameter WL_CHECK = 3'h1;
parameter WL_SWAP = 3'h2;
parameter WL_UPDATE = 3'h3;
wire [9:0] logical_block = logical_address[31:22];
wire [9:0] physical_block;
// Minimalen und aktuellen Löschzähler finden
reg [23:0] min_erase_count;
reg [23:0] current_erase_count;
reg need_leveling;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
wl_state <= WL_IDLE;
operation_complete <= 1'b0;
wear_warning <= 1'b0;
end
else begin
case (wl_state)
WL_IDLE: begin
if (write_enable) begin
wl_state <= WL_CHECK;
current_erase_count <= erase_count[logical_to_physical[logical_block]];
end
end
WL_CHECK: begin
// SICHER: Prüfen, ob Wear Leveling erforderlich
if ((current_erase_count - min_erase_count) > WEAR_THRESHOLD) begin
need_leveling <= 1'b1;
wl_state <= WL_SWAP;
end else begin
wl_state <= WL_UPDATE;
end
end
WL_SWAP: begin
// Blocktausch für Wear Leveling durchführen
perform_block_swap();
wl_state <= WL_UPDATE;
end
WL_UPDATE: begin
// Schreibvorgang durchführen und Löschzähler aktualisieren
flash_write(logical_to_physical[logical_block], write_data);
erase_count[logical_to_physical[logical_block]] <=
erase_count[logical_to_physical[logical_block]] + 1;
operation_complete <= 1'b1;
wl_state <= WL_IDLE;
// Warnung bei Annäherung an Lebensdauerende
if (current_erase_count > 24'd90000) begin
wear_warning <= 1'b1;
end
end
endcase
end
end
// Ratenbegrenzung zur Verhinderung beschleunigter Verschleißangriffe
reg [31:0] write_counter;
reg [31:0] last_write_time;
parameter WRITE_RATE_LIMIT = 32'd1000; // Maximale Schreibvorgänge pro Sekunde
always @(posedge clk) begin
if (write_enable) begin
if ((current_time - last_write_time) < WRITE_RATE_LIMIT) begin
// SICHER: Schreibvorgänge ratenbegrenzen, um Verschleißangriffe zu verhindern
operation_complete <= 1'b0; // Schreibvorgang ablehnen
end else begin
last_write_time <= current_time;
end
end
end
endmodule
CVE-Beispiele
Wear-Leveling-Schwachstellen wurden in verschiedenen Speichersystemen gefunden, bei denen Angreifer die Speicherdegradation durch konzentrierte Schreibmuster beschleunigen könnten.
Verwandte CWEs
- CWE-400: Unkontrollierter Ressourcenverbrauch (übergeordnet)
- CWE-1202: Speicher- und Speicherprobleme (Kategoriemitglied)
Referenzen
- MITRE Corporation. "CWE-1246: Improper Write Handling in Limited-write Non-Volatile Memories." https://cwe.mitre.org/data/definitions/1246.html
- Qureshi et al.: "Enhancing Lifetime and Security of PCM-Based Main Memory with Start-Gap Wear Leveling"
- Micron: "Bad Block Management in NAND Flash Memory"