Der im F-35 verwendete C++-Standard – warum Kampfjets 90 % der C++-Funktionen verbieten
(youtube.com)- Ein Video erklärt, warum Kampfjet- und Raketen-Software, bei der ein einzelner Bug bei Mach 1 fatale Folgen haben kann, den Großteil der C++-Funktionen entfernt und nur vorhersagbaren Code übriglässt
- Es fasst die Geschichte zusammen, wie der mechanische Bombencomputer der F-4, der lange geheime Mikroprozessor der F-14 und die militärischen Sprachkämpfe rund um Jovial, CMS-2 und Ada sowie die Code-Explosion zur Forderung nach einer einheitlichen sicheren Sprache und strengen Standards führten
- Es demonstriert anhand echten Codes, wie der im Zuge der F-35-Entwicklung entstandene JSF-C++-Standard, mit dem Lockheed den Einsatz von C++ statt Ada durchsetzte, Ausnahmen, Rekursion und dynamische Speicherallokation verbietet und sie durch Rückgabecodes, Schleifen und vorab reservierten Speicher ersetzt
- Außerdem wird gezeigt, dass die frühen Missionscomputer der F-35 eine PowerPC-basierte Architektur nutzten, und mithilfe einer Verbindung von X-Plane 12 mit einem selbstgebauten MFD verglichen, wie sich Code, der die JSF-Regeln verletzt, und regelkonformer Code tatsächlich im Flug unterschiedlich verhalten
- Abschließend wird dargestellt, dass der JSF-Standard später zu Sicherheitsstandards wie NASA F-Prime, MISRA und AutoSAR führte und dass heute ein Ansatz auf Basis dieses Erbes mit C++ Core Guidelines und modernem C++ passender ist
Vorstellung des Vortragenden
- Ein Entwickler, der C++-Code für Luft- und Raumfahrtsysteme geschrieben und Demos für die Luftwaffe durchgeführt hat
- Er erklärt das Thema auf Grundlage realer Erfahrung mit C++ in Systemen mit hohen Sicherheitsanforderungen
- Als Demo-Umgebung nutzt er ein selbstgebautes MFD (Multifunction Display), bestehend aus X-Plane 12, einer Web-API, einer Python-UI und einem C++-Backend
Flugsoftware und Umgebungen, in denen Fehler nicht toleriert werden
- Bei gewöhnlichen Anwendungen endet ein Crash oft mit einem Neustart, aber bei Mach-1-Kampfjets und Raketen bedeutet ein einziger Fehler unmittelbar eine Katastrophe
- Mit dem Satz „Bei Mach 1 gibt es keine Zeit, auf den Garbage Collector zu warten“ wird die Echtzeitanforderung betont
- Wenn eine einzige fehlerhafte Zeile zu tödlichen Folgen führen kann, muss bereits die Auswahl der Sprachfeatures selbst als Sicherheitsmechanismus dienen
- Als typisches Beispiel wird die Explosion der Ariane 5 im Jahr 1996 genannt
- Beim Umwandeln eines 64-Bit-Gleitkommawerts für den horizontalen Bias in eine 16-Bit-Ganzzahl trat eine Ausnahme auf, die nicht behandelt wurde
- Die Sprache erzeugte den Fehler normgerecht, aber das System konnte die Ausnahme nicht verarbeiten, was zur sofortigen Zerstörung einer 500-Millionen-Dollar-Rakete führte
- Ausgehend von diesem Fall entschied sich das F-35-Designteam für den Ansatz, Sprachfeatures selbst abzuschneiden, um denselben Fehler nicht zu wiederholen
Geschichte militärischer Software und der Sprachenkrieg
- In der Frühzeit von Kampfflugzeugen wie der F-4 Phantom gab es praktisch noch keine Software
- Der Bombencomputer war eher eine präzise mechanische Vorrichtung aus Zahnrädern und Nocken, und der „Code“ war die Form der Metallnocken selbst
- Beim geheimen Marine-Luftüberlegenheitsprojekt VFX (F-14 Tomcat) änderte sich die Lage
- Erst später wurde bekannt, dass Garrett AiResearch einen Mikroprozessor für die F-14 entwickelt hatte, der der als „erster Mikroprozessor“ bekannten Intel 4004 aus Lehrbüchern zeitlich vorausging
- Um den Schwenkflügel der F-14 optimal zu steuern, war ein leistungsfähiger Mikroprozessor nötig, auf dem rund 2500 Zeilen Microcode für Polynom-Berechnungen liefen
- Danach begann durch die Einführung unterschiedlicher Sprachen bei den Teilstreitkräften ein Wildwuchs an Programmiersprachen
- Die Luftwaffe nutzte Jovial (Jules Own Version of the International Algorithmic Language), eine Sprache aus der ALGOL-Familie
- Die Marine wollte die Sprache der Luftwaffe nicht verwenden und setzte in der F-18 auf CMS-2
- Durch unterschiedliche Sprachen und Hardware-Architekturen wurde Code-Wiederverwendung und Verifikation nahezu unmöglich
- Gleichzeitig wuchs die Softwaregröße pro Flugzeug exponentiell
- Als Zahlenbeispiele nennt der Vortrag etwa 125.000 Zeilen für die F-16A, etwa 1 Million für die B-1 und rund 9 Millionen für die moderne F-35
- Eine Untersuchung des Verteidigungsministeriums ergab, dass mehr als 450 Programmiersprachen im Einsatz waren und kaum eine davon über einen vernünftigen Standard verfügte
Die Durchsetzung von Ada und ihre Grenzen
- Um dieses Chaos zu beheben, entwickelte das Verteidigungsministerium mit Ada eine einheitliche Hochsprache und verpflichtete Projekte stark zu ihrer Nutzung
- Wer in neuen Projekten kein Ada einsetzen wollte, musste nachweisen, warum es mit Ada unmöglich war; andernfalls war kein Vertragszuschlag zu bekommen
- Ada wird als Sprache vorgestellt, die für Bereiche mit hohen Anforderungen an Sicherheit und Zuverlässigkeit sehr gut geeignet ist
- Besonders hervorgehoben wird das Design zur Gewährleistung von Speicher- und Typsicherheit in sicherheitskritischen Systemen wie der Luft- und Raumfahrt
- In den 1990er-Jahren zeigte sich jedoch eine zunehmende Entkopplung von der Realität
- Gleichzeitig dominierten Internet, Windows 95 und kommerzielle Spiele auf Basis von C++ den Mainstream
- Studierende und Entwickler wandten sich statt teurer Ada-Compiler ganz natürlich dem kostenlosen GCC und C++ zu
- Ada-Compiler kosteten mehrere tausend Dollar und waren für Einzelpersonen schwer zugänglich, wodurch auch der Pool an Ada-Fachkräften schrumpfte
F-35 und die Entstehung des JSF-C++-Standards
- Die F-35 (Joint Strike Fighter) wurde von Anfang an als Flugzeug mit sehr hohem Softwareanteil konzipiert
- Komplexe Berechnungen wie Sensor Fusion wurden zu einem Kernbestandteil des Betriebs
- Lockheed Martin schlug dem Verteidigungsministerium vor, für diese Anforderungen den Einsatz von C++ zu erlauben
- Das lief faktisch auf die Bitte hinaus, die bisherige Ada-Pflicht zu lockern, und dafür musste ein Weg gefunden werden, die Risiken von C++ zu beherrschen
- Dabei wirkte auch der C++-Erfinder Bjarne Stroustrup als Berater an der Gestaltung der JSF-C++-Regeln mit
- Er erklärte, direkt an der Ausarbeitung beteiligt gewesen zu sein, und sagte selbst, dass er diesem Standard gegenüber daher „voreingenommen sein könnte“
- Die Kernidee ähnelt dem Gedanken hinter einem „remove before flight“-Tag
- Wie ein Tag, der vor dem Start entfernt werden muss, werden gefährliche C++-Funktionen auf Sprachebene entfernt, sodass nur eine vorhersagbare Teilmenge verwendet wird
- So sollte Ada-ähnliche Sicherheit erreicht werden, während Entwickler dennoch einen Teil der Ausdrucksstärke von C++ nutzen können
Reale Hardware und die GameCube-Analogie
- Die frühen F-35-Blöcke verwendeten Missionscomputer auf Basis von Motorola-G4-PowerPC-Prozessoren
- Diese Information wurde unter anderem in einem Artikel von Aviation Today aus dem Jahr 2003 veröffentlicht
- Auch der GameCube nutzt einen Prozessor aus der PowerPC-Familie, was als interessante hardwareseitige Verwandtschaft auf Befehlssatzebene angeführt wird
- Für eine exaktere Generationseinordnung wäre eine andere Konsole näherliegend, aber zum Verständnis des Prinzips reicht der GameCube-Vergleich aus
JSF-C++-Demo-Umgebung: X-Plane 12 + MFD
- Auf Basis von X-Plane 12 und dem F-35B-Addon von AOA Simulations wird eine Flugsimulator-Umgebung aufgebaut
- Über die neue Web-API von X-Plane werden Flugdatendaten in Echtzeit abonniert und im MFD angezeigt
- Das Frontend besteht aus Python, während das Backend als C++-Plugin umgesetzt ist; so werden regelkonformer und regelverletzender Code direkt verglichen
- Angezeigt werden unter anderem Höhe, Geschwindigkeit, Wind, Flight Envelope und Navigationsdaten als Ergebnisse von C++-Berechnungen
- Im Flug wird absichtlich nicht standardkonformer C++-Code, der Ausnahmen wirft, ausgeführt, sodass das MFD abstürzt und Probleme im Flug entstehen; anschließend wird schrittweise gezeigt, wie die Anwendung der JSF-Regeln dies behebt
Die drei zentralen Einschränkungen von JSF: Ausnahmen, Rekursion, dynamischer Speicher
- Der JSF-C++-Standard betont drei zentrale Einschränkungen: Ausnahmen (Exceptions), Rekursion und dynamische Speicherallokation
- Zusätzlich wird auch eine Obergrenze für die Cyclomatic Complexity von Funktionen festgelegt
1) Verbot von Ausnahmen – AV Rule 208
- JSF AV Rule 208: „Exceptions shall not be used“
- Schlüsselwörter wie
try,catchundthrowwerden vollständig verboten
- Schlüsselwörter wie
- Der wichtigste Grund ist die Nichtdeterministik des Kontrollflusses
- Wie bei Ariane 5 kann das gesamte System unvorhersehbar zusammenbrechen, wenn bei einer Ausnahme das Handling fehlt oder das Timing nicht stimmt
- Bjarne steht moderner C++-Exception-Handling grundsätzlich positiv gegenüber, erklärt aber, dass zur Zeit der JSF-Entwicklung Werkzeugreife und garantierte Echtzeitfähigkeit für eine Unterstützung noch nicht ausreichten
„JSF++ ist für Hard-Real-Time- und sicherheitskritische Anwendungen (Flugsteuerung) gedacht. Wenn Berechnungen zu lange dauern, können Menschen sterben, und mit Ausnahmen lassen sich Antwortzeiten nicht garantieren.“
- Statt Ausnahmen verwendet JSF Rückgabecodes
- Im Beispiel einer Berechnung der Druckhöhe werden für verschiedene Fehlersituationen unterschiedliche Rückgabecodes gesetzt, die der Aufrufer auswertet
- So kann die aufrufende Seite auch bei Berechnungsfehlern oder Zeitüberschreitungen einen sicheren „Fehlerzustand“ darstellen, ohne dass das gesamte Programm abstürzt
2) Verbot von Rekursion – AV Rule 119
- AV Rule 119 verbietet, dass eine Funktion direkt oder indirekt sich selbst aufruft, also Rekursion
- Da sich bei jedem rekursiven Aufruf ein neuer Stack-Frame aufbaut und die maximale Tiefe schwer zu begrenzen ist, steigt das Risiko eines Stack Overflow
- Als Beispiel dient der Binomialkoeffizient, der bei der Berechnung eines Ausweichflughafens im Flugplan verwendet wird
- In der nicht standardkonformen Version wird eine rekursive Implementierung in der Form
C(n, k) = C(n-1, k-1) + C(n-1, k)verwendet - Dadurch hängt die Aufruftiefe von der Eingabe ab, was die Vorhersage der Speicherobergrenze erschwert
- In der nicht standardkonformen Version wird eine rekursive Implementierung in der Form
- In der JSF-konformen Version wird dieselbe Berechnung auf eine iterative Implementierung mit Schleifen umgestellt
- Der Code ist länger und weniger elegant, liefert aber ohne Selbstaufrufe dasselbe Ergebnis
- Dadurch lassen sich maximale Aufruftiefe und Speicherverbrauch statisch deutlich leichter abschätzen
3) Verbot dynamischer Speicherallokation – AV Rule 206
- AV Rule 206: Nach der Initialisierung werden weder Speicher angefordert noch freigegeben
- Heap-Allokationen zur Laufzeit über
new,deleteoder internesnewüber Smart Pointer sind verboten
- Heap-Allokationen zur Laufzeit über
- Der Grund sind zwei zentrale Probleme
- Heap-Allokation bringt eine zeitliche Nichtdeterministik mit sich, weil unklar ist, wie lange sie dauert
- Wird der Heap fragmentiert, kann trotz noch freier Gesamtkapazität die Zuweisung scheitern, weil kein großer zusammenhängender Block mehr gefunden wird
- Als Beispiel wird nicht standardkonformer Code gezeigt, der für die Berechnung von Böen eine Verlaufspuffer für IAS (Indicated Airspeed) mit
std::unique_ptrund einem dynamischen Array anlegt- Dabei wird zur Laufzeit ein neues Array angefordert und damit gegen die JSF-Regeln verstoßen
- Die JSF-konforme Version verwendet stattdessen ein Array fester Größe mit konstant definierter Maximalgröße
- Mit Konstanten wie
MAX_IAS_HISTORYwird die Größe festgelegt, der Speicher einmalig bei der Initialisierung reserviert und anschließend nur der Index rotiert - Dadurch entstehen im laufenden Betrieb keine weiteren Allokationen mehr, was Vorhersagbarkeit bei Zeit und Speicher sicherstellt
- Mit Konstanten wie
4) Obergrenze für Cyclomatic Complexity – AV Rule 3
- AV Rule 3 schreibt vor, dass die Cyclomatic Complexity einer Funktion 20 nicht überschreiten darf
- Die Funktionsdeklaration zählt selbst mit 1 Punkt, und
if,while,for,switchsowie logische AND/OR erhöhen die Komplexität jeweils um 1
- Die Funktionsdeklaration zählt selbst mit 1 Punkt, und
- An der iterativen Implementierung des Binomialkoeffizienten wird gezeigt, wie jedes
if, jede logische Operation und jedefor-Schleife den Komplexitätswert erhöht- Da mit steigender Komplexität Test, Verifikation und Analyse schwieriger werden, ist die Einhaltung einer Obergrenze ein zentrales Ziel des Standards
Das JSF-Erbe: NASA F-Prime, MISRA, AutoSAR
- Die Ideen des JSF-C++-Standards verbreiteten sich später in andere sicherheitskritische Bereiche
- Das Flugsoftware-Framework F-Prime der NASA wurde 2017 veröffentlicht und teilt Regeln wie Verbot dynamischer Speicherallokation, Verbot von Ausnahmen und Verbot von Rekursion
- Auch in der Automobilindustrie setzte sich ein ähnlicher Trend fort
- Standards wie MISRA C++ und AutoSAR (Automotive Open System Architecture) entstanden und legten Sicherheitsregeln für Fahrzeugsoftware fest
- Es wird erwähnt, dass die AutoSAR-C++14-Guidelines ausdrücklich auf JSF Bezug nehmen, was zeigt, dass der Einfluss von JSF bis in die Automotive-Software reicht
- Moderne Autos sind faktisch „Computer auf Rädern“, sodass solche Sprach-Teilmengen und Coding-Regeln eine zentrale Grundlage für Sicherheit bilden
Fazit: Woran sollte man sich heute orientieren, wenn man C++ verwendet?
- Der JSF-C++-Standard wird als technische Leistung dargestellt, die für ihre Zeit eine komplexe Sprache auf eine vorhersagbare Teilmenge reduzierte und so Sicherheit auf dem Niveau von Flugsteuerung in Kampfjets ermöglichte
- Gleichzeitig empfiehlt Bjarne Stroustrup heutigen Entwicklern, sich an den C++ Core Guidelines und modernem C++ zu orientieren
- Denn Sprache und Toolchain von C++ haben sich in den vergangenen Jahrzehnten weiterentwickelt, und für Features wie Ausnahmen und Smart Pointer gibt es heute oft sichere Einsatzumgebungen
- Trotzdem bleibt JSF ein wichtiges Beispiel für die Denkweise, eine Sprache nicht durch Ergänzen, sondern durch „Entfernen“ zu kontrollieren
- Die abschließende Botschaft lautet: Nicht was man hinzufügt, sondern was auf die „remove before flight“-Liste gehört, ist der Kern des Entwurfs sicherheitskritischer Systeme
Noch keine Kommentare.