Memory Scrubbing
Memory Scrubbing ist ein präventiver Integritätsmechanismus, der das Code-Segment im RAM periodisch auf Bit-Flips prüft — bevor korrumpierte Instruktionen ausgeführt werden. Er läuft als Hintergrundprozess und ist auf Systemen sinnvoll, die kosmischer Strahlung ausgesetzt sind (Luft- und Raumfahrt) oder bei denen SRAM-Fehler durch Alterung und Temperatur auftreten können.
Im Unterschied zu Software Lockstep schützt Memory Scrubbing nicht die Berechnung (CPU-Ebene), sondern den gespeicherten Maschinencode (Speicher-Ebene). Beide Mechanismen sind komplementär.
→ Verwandt: TMR · Software Lockstep · .meta_safe ELF-Sektion · DO-178C Hauptseite
1. Aktivierung
// Unit-Level — schützt alle Funktionen der Unit
@integrity(mode: scrubbed, interval: 100)
unit nav.core;
import std.io;
- mode: scrubbed — aktiviert den periodischen CRC32-Sweep des Code-Segments
- interval: 100 — alle 100 ms einen vollständigen Sweep durchführen
Der interval-Wert ist ein Richtwert für die Runtime/den Scheduler. Auf Bare-Metal-Targets mit SysTick wird der Sweep in der SysTick-ISR ausgelöst.
2. Wie der Compiler vorbereitet
Der Scrubbing-Mechanismus setzt voraus, dass der Compiler beim Build eine kryptographische Referenz des Code-Segments einbettet:
- Code-Generierung: Backend erzeugt vollständigen Maschinencode.
- Sektion anlegen: Die
.meta_safeELF-Sektion wird mit Platzhaltern angelegt. - CRC32-Berechnung: Der Compiler berechnet den CRC32 (IEEE 802.3) über das fertige Code-Segment.
- Post-Patching: Die drei Hashslots in
.meta_safewerden mit dem echten Wert überschrieben.
Die drei Kopien liegen 4096 Byte voneinander entfernt — ein lokaler Speicherdefekt kann nicht alle drei gleichzeitig korrumpieren.
→ Vollständige Struktur der .meta_safe Sektion: .meta_safe ELF-Sektion
3. Laufzeit-Ablauf
Beim Programmstart und in regelmäßigen Abständen:
Code-Segment im RAM
┌─────────────────────────────────────────────────────┐
│ fn ComputeFlightPath() { … } │
│ fn ReadIMU() { … } │
│ … alle Instruktionen der Unit … │
└─────────────────────────────────────────────────────┘
│
│ CRC32 berechnen (periodisch)
▼
Ist-Hash (neu berechnet)
│
│ Vergleich gegen alle drei Referenzen
▼
.meta_safe ELF-Sektion
┌────────────────────────────────────────────────────┐
│ hash_copy_1 @ Offset 32 (CRC32: 0xA3F2...) │
│ [4096 Byte Padding] │
│ hash_copy_2 @ Offset 4128 (CRC32: 0xA3F2...) │
│ [4096 Byte Padding] │
│ hash_copy_3 @ Offset 8224 (CRC32: 0xA3F2...) │
└────────────────────────────────────────────────────┘
│
Mehrheitsentscheid (TMR):
≥ 2 von 3 stimmen überein → ✅ OK
< 2 übereinstimmend → ⚠ Integritätsfehler
4. VerifyIntegrity()
Das Builtin VerifyIntegrity() löst einen sofortigen manuellen Sweep aus und gibt bool zurück:
@integrity(mode: scrubbed, interval: 100)
unit flight_control;
import std.io;
fn StartupCheck(): void {
if (VerifyIntegrity() = false) {
// Bit-Flip erkannt — vor dem normalen Betrieb
PrintStr("INTEGRITY FAILURE — switching to backup\n");
ActivateBackupComputer();
HaltPrimary();
}
PrintStr("Code integrity verified\n");
}
fn main(): int64 {
StartupCheck(); // Immer zuerst
// ... normale Betriebslogik ...
return 0;
}
VerifyIntegrity() ist das erste, was ein DAL-A-System beim Start aufruft — bevor irgendwelche sicherheitskritischen Berechnungen beginnen.
5. Integration mit dem Scheduler / ISR
Auf Bare-Metal-Targets ohne RTOS löst der SysTick-ISR den periodischen Sweep aus:
unit cortexm_scrub;
@volatile var scrub_tick: int64 := 0;
con SCRUB_INTERVAL_MS: int64 := 100;
@section(".isr_vector")
@no_opt
fn SysTick_Handler(): void {
scrub_tick := scrub_tick + 1;
}
fn main(): int64 {
// Startup-Check
if (VerifyIntegrity() = false) {
panic("boot integrity failure");
}
var last_scrub: int64 := 0;
while (true) {
// Anderen Aufgaben verarbeiten ...
DoControlCycle();
// Periodischer Scrub
if (scrub_tick - last_scrub >= SCRUB_INTERVAL_MS) {
last_scrub := scrub_tick;
if (VerifyIntegrity() = false) {
// Bit-Flip im Code erkannt — Recovery
ActivateBackupSystem();
}
}
}
return 0;
}
Auf FreeRTOS (ESP32) wird der Sweep in einer dedizierten Low-Priority-Task ausgeführt:
fn ScrubTask(arg: int64): void {
while (true) {
vTaskDelay(100); // 100 RTOS-Ticks ≈ 100 ms
if (VerifyIntegrity() = false) {
ActivateBackupSystem();
}
}
}
6. Timing-Fenster
Zwischen zwei Sweeps besteht ein ungeschütztes Zeitfenster: Ein Bit-Flip, der nach einem erfolgreichen Sweep entsteht, wird erst beim nächsten Sweep erkannt. Dieses Fenster ist durch interval konfigurierbar.
| Interval | Ungeschütztes Fenster | CRC-Overhead (typisch) |
|---|---|---|
| 10 ms | Max. 10 ms | ~5–15 µs pro Sweep (je nach Code-Segment-Größe) |
| 100 ms | Max. 100 ms | Vernachlässigbar |
| 1000 ms | Max. 1 s | Minimalster Overhead |
Für DAL-A empfiehlt sich ein Interval von 50–100 ms. Der CRC-Overhead ist proportional zur Größe des Code-Segments und liegt bei typischen Avionik-Units (< 64 kB Code) unter 20 µs.
7. Kombination mit anderen Schutzmechanismen
// Maximaler Schutz: Unit mit Scrubbing + kritische Funktionen mit Lockstep + @redundant Daten
@integrity(mode: scrubbed, interval: 50)
unit autopilot.kernel;
@redundant
var flight_mode: int64 := 0;
@dal(A)
@flight_crit
@integrity(mode: software_lockstep, interval: 50)
@wcet(200)
@stack_limit(2048)
fn ComputeFlightCommands(state: ^FlightState): FlightCommands {
// Berechnung geschützt durch: Lockstep (ALU) + Scrubbing (Code-Segment) + TMR (Daten)
// Das ist die Dreifach-Absicherung für DAL-A-Kernfunktionen
}
| Mechanismus | Was er schützt | Ergänzt |
|---|---|---|
@integrity(scrubbed) | Code-Segment (Bit-Flip in Instruktionen) | Lockstep, TMR |
@integrity(software_lockstep) | Berechnungen (Latch-Fehler in der CPU) | Scrubbing, TMR |
@redundant | Kritische Datenwerte (Bit-Flip im RAM) | Scrubbing, Lockstep |
8. DAL-Empfehlung
| DAL | Memory Scrubbing | Begründung |
|---|---|---|
| A | Zwingend | Nachweis gegen Single-Point-Failure im Code-Speicher |
| B | Empfohlen | SEU-Risiko rechtfertigt Overhead |
| C | Optional | Sinnvoll auf strahlungsexponierten Targets |
| D/E | Nicht nötig | Kein SEU-Schutz gefordert |
Letzte Aktualisierung: 2026-05-22
