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

 
Seit 1.1.15A gibt es den Mechanismus — der frühere Kasten („byte-gleiches Programm„, #1878, #1879, gemessen mit 1.1.14A) ist überholt. Nachgemessen (gemessen mit lyxc 1.1.18A, 2026-09-05, #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; 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:

  1. Code-Generierung: Backend erzeugt vollständigen Maschinencode.
  2. Sektion anlegen: Die .meta_safe ELF-Sektion wird mit Platzhaltern angelegt.
  3. CRC32-Berechnung: Der Compiler berechnet den CRC32 (IEEE 802.3) über das fertige Code-Segment.
  4. 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: .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, → 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 (#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).