====== 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 [[lyx_-_programmiersprache:guides:do-178c:software-lockstep|Software Lockstep]] schützt Memory Scrubbing nicht die **Berechnung** (CPU-Ebene), sondern den **gespeicherten Maschinencode** (Speicher-Ebene). Beide Mechanismen sind komplementär.
→ Verwandt: [[lyx_-_programmiersprache:guides:do-178c:triple_modular_redundancy|TMR]] · [[lyx_-_programmiersprache:guides:do-178c:software-lockstep|Software Lockstep]] · [[lyx_-_programmiersprache:guides:do-178c:meta_safe|.meta_safe ELF-Sektion]] · [[lyx_-_programmiersprache:guides:do-178c|DO-178C Hauptseite]]
> **Seit 1.1.15A gibt es den Mechanismus** — der frühere Kasten („byte-gleiches Programm", [[https://github.com/SEOLizer/LyX-Compiler/issues/1878|#1878]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1879|#1879]], gemessen mit 1.1.14A) ist überholt. Nachgemessen (gemessen mit lyxc 1.1.18A, 2026-09-05, [[https://github.com/SEOLizer/LyX-Compiler/issues/1908|#1908]]):
>
> * ''@integrity(mode: scrubbed, interval: 100)'' erzeugt **keine** Warnung mehr und ein deutlich anderes Erzeugnis (4238 → 17008 Byte am Minimalprogramm). Im Datenbereich liegt eine Hashtabelle mit der Kennung ''METASAF2'', und ein SIGALRM-Zeitgeber prüft periodisch die **geladenen** Codeseiten gegen CRC32-Referenzen — ohne Dateizugriff.
> * Die Tabelle liegt **dreifach** ab (Abstand ''stride''). Ein Single-Event-Upset verfälscht höchstens eine Kopie, die Mehrheit entscheidet. Abbruch **135** bei verfälschter Codeseite, **136** wenn keine Mehrheit zustande kommt.
> * ''interval'': 1…3600000 ms, ohne Angabe **1000 ms**. Mehrere ''scrubbed''-Angaben in einem Programm: das **kleinste** Intervall gewinnt — ein Zeitgeber je Prozess kann nur eine Frist tragen.
> * ''scrubbed'' gibt ''setitimer'' im seccomp-Filter frei. Steht das Attribut in einer **importierten** Einheit und führt das Programm ''@capabilities'', wird das gemeldet: der Filter steht zu dem Zeitpunkt schon.
> * **Erkennen ist weiterhin nicht Korrigieren.** ''recovery_ptr'' im Format wird nicht angesprungen; eine zweite Codekopie gibt es nicht.
> * **Nur x86-64** — die IR-Backends (lyxos, arm64, riscv, arm-cm4, xtensa) tragen keinen Sweep.
>
> Davon getrennt: ''%--%meta-safe'' legt dieselben CRC32-Prüfsummen in der Sektion ''.meta_safe'' ab; [[lyx_-_programmiersprache:units:meta_safe|std.meta_safe]] prüft damit **die Datei** über ''/proc/self/exe'' und **auf Zuruf**. Zwei Wege, zwei Fragen: Manipulation der Datei gegen Bit-Flip im geladenen Code.
----
===== 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_safe'' ELF-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_safe'' werden 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: [[lyx_-_programmiersprache:guides:do-178c:meta_safe|.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
PrintLn("INTEGRITY FAILURE — switching to backup");
ActivateBackupComputer();
HaltPrimary();
}
PrintLn("Code integrity verified");
}
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:
> **''@section'' und ''@no_opt'' gibt es nicht** — beide werden mit ''unbekanntes Attribut'' abgewiesen. Die Platzierung im Interrupt-Vektor übernimmt heute das Linker-Skript der Zielplattform, nicht der Quelltext; eine Möglichkeit, den Codegen je Funktion zu bremsen, gibt es nicht (''%%--%%no-opt'' wirkt nur programmweit, → [[lyx_-_programmiersprache:tools:compiler-parameter|CLI-Referenz]]).
>
> Für den Zugriff auf ein Hardware-Register bleibt ''@volatile'' — das ist vorhanden und wirkt.
unit cortexm_scrub;
@volatile var scrub_tick: int64 := 0;
con SCRUB_INTERVAL_MS: int64 := 100;
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: Scrubbing + kritische Funktionen mit Lockstep + @redundant Daten
// Achtung: software_lockstep gilt NUR an einer Funktion und kennt kein interval
@integrity(mode: scrubbed, interval: 50)
unit autopilot.kernel;
@redundant
var flight_mode: int64 := 0;
@dal(A)
@flight_crit
@integrity(mode: software_lockstep)
@wcet(200)
@stack_limit(2048)
fn ComputeFlightCommands(state: int64): 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(mode: scrubbed)'' | Code-Segment (Bit-Flip in Instruktionen) | Lockstep, TMR |
| ''@integrity(mode: software_lockstep)'' | den Rückgabeausdruck einer Funktion | 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-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1908|#1908]], gemessen mit lyxc 1.1.18A) — Zielbild-Kasten ersetzt: der Sweep existiert seit 1.1.15A (''METASAF2''-Hashtabelle, SIGALRM, Abbruch 135/136, dreifache Ablage) — der frühere Befund „byte-gleiches Programm" ist überholt. ''interval'' aus dem ''software_lockstep''-Aufruf in Abschnitt 7 entfernt, Kurzformen der Vergleichstabelle auf die gültige Schreibweise mit ''mode:'' gezogen.
Vorherige letzte Aktualisierung: 2026-05-22
Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).
Letzte Aktualisierung: 2026-08-30 — gegen ''lyxc 1.1.14A'' nachgemessen: ''@integrity'' ist wirkungslos, ein periodischer Sweep existiert nicht; die Seite ist als Zielbild gekennzeichnet (#1878, #1879).