Unser alltägliches Leben wird zunehmend von Technologien bestimmt, daher gewinnt auch die Sicherheit von Embedded-Systemen immer mehr an Bedeutung. Dieser Beitrag wirft einen kurzen Blick auf die weitverbreiteten Paradigmen rund um die Safety-orientierte Entwicklung und zeigt, wie sich der Zertifizierungsprozess beschleunigen lässt.

In der Entwicklung von Embedded-Systemen werden in erster Linie die Programmiersprachen C und C++ eingesetzt. Der Hauptgrund für die anhaltend weite Verbreitung dieser Sprachen ist die Tatsache, dass es eine breite Tool-Unterstützung gibt, die nahezu jede Embedded-Architektur abdeckt. Außerdem werden C und C++ bereits so lange genutzt, dass die Entwickler ein tiefgreifendes Verständnis für die Nuancen der Sprachen entwickelt haben. Darüber hinaus helfen viele Tools für statische Analyse dabei, potenzielle Fehler im Code zu lokalisieren und beheben. Nicht zuletzt sind die Sprachen – vor allem C – sehr flexibel, sodass der Entwickler hardwarenah Register auslesen und I/O-Leitungen umschalten oder das Verhalten zwischen Strukturen und Objekten auf höheren Ebenen betrachten kann. All dies erleichtert die tägliche Arbeit eines Entwicklers.

Wie sicher ist C?

Wenn C und C++ sich also so bewährt haben, warum sollte es problematisch sein, sie auch in der Entwicklung von Systemen mit sicherheitskritischen Funktionen einzusetzen? Tatsächlich gibt es einige Stolperfallen, die sich aber umgehen lassen. In der Tabelle C4.5 des internationalen Dachstandards für funktionale Sicherheit (IEC 61508) etwa lautet eine Empfehlung, dass die für ein sicherheitskritisches System gewählte Sprache vollständig und eindeutig definiert sein sollte. Nun gibt es rund 190 verschiedene undefinierte Verhaltensweisen, in denen der Compiler entscheidet, wie die Quelleprogrammierung zu interpretieren ist. Zum Beispiel, wenn es um den Zugang eines Zeigerwertes zu einem Objekt geht, dessen Lebensdauer bereits überschritten ist, oder wenn zwei Angaben zu demselben Objekt oder derselben Funktion nicht-kompatible Typen spezifizieren.

Der Standard besagt auch, dass die Sprache die Nutzung von kleinen und beherrschbaren Softwaremodulen unterstützen sollte. In C und C++ ist das möglich, aber es ist keine Pflicht. Dasselbe trifft auf die Anforderungen der IEC 61508 zu, nach denen der Zugang zu Daten in speziellen Softwaremodulen sowie der Definition variabler Teilbereiche beschränkt sein sollte. Trotzdem sind C und C++ mit dem richtigen Sprachsubset ein höchst empfehlenswerter Standard für alle vier Safety Integrity Levels (SIL) von IEC 61508.

MISRA C/C++ bekämpft Nebenwirkungen

Ein perfekter Ausgangspunkt für ein solches Subset ist MISRA C/C++. Obwohl dies in erster Linie ein Programmierstandard für die Automobilindustrie ist, nutzen ihn auch viele andere Branchen als Basis für einen Codierungsstandard. MISRA ist ein Sprachsubset, das darauf abzielt alle Mehrdeutigkeiten der Programmiersprache zu beseitigen, und ein sicheres und zuverlässiges Verhalten des Codes zu unterstützen.

Zum Beispiel legt die MISRA C 2004 Regel 33 (required) fest: „the right-hand side of an && or || operator shall not contain side effects” (die rechte Seite eines && oder || Operators sollte keine Nebeneffekte beinhalten). Welche Bedeutung diese Vorschrift hat, wird bei Betrachtung der folgenden Code-Zeile deutlich:

if ((x == y) || (*p++ == z))

Die Spezifizierung der C-Sprache hält den Entwickler nicht davon ab, einen solchen Code zu entwerfen. Trotzdem setzt das ein tiefgehendes Verständnis für die Sprachregeln voraus, denn die rechte Seite wird nur evaluiert (und verursacht auch Nebenwirkungen), wenn der Code auf der linken Seite falsch ist. Das bedeutet, dass der Post-Inkrement-Zeiger nur dann ausgeführt wird, wenn die linke Seite falsch ist. Auch wenn diese vielleicht das beabsichtigte Verhalten darstellt, schleichen sich hier ganz leicht Fehler ein, und das bedeutet auch, dass jeder, der den Code prüft oder wartet, diese Nuance der C-Sprache verstehen muss.

Konstrukte dieser Art sind in MISRA verboten, weil sie so fehleranfällig sind. Ein weniger erfahrener Nutzer oder Prüfer könnte annehmen, dass der Zeiger immer erhöht wird, und wegen seines mangelnden Verständnisses, wie der Sprachstandard mit dieser Situation umgeht, falsche Berechnungen vornehmen und somit falsche Daten eingeben, die möglicherweise zu einem Absturz der Anwendung führen können.

Ein weiteres gutes Beispiel ist die MISRA C 2004 Regel 8, die bestimmt, dass der Speicher zum Beispiel zum Zeitpunkt der Kompilierung statisch festgelegt werden muss. Das ist vielleicht eine lästige Aufgabe, aber sie dient der erhöhten Sicherheit, weil sie sicherstellt, dass das System während seiner Laufzeit immer über ausreichend Speicher verfügt und keine Kapazitäten aus dem Heap zieht. Vielmehr ist dies die Aufgabe des Compilers, und so zeigen sich mögliche Probleme mit dem Speichermanagement noch vor der Testphase. Denn wenn ein System nicht mehr ausreichend Speicher zur Verfügung hat, wird es sich entweder sperren oder neu starten, um so den Fehler zu beheben – ein Verhalten, das zum Beispiel bei einem Luftfahrtsystem mitten in einem Flug auf keinen Fall auftreten sollte.

 

Lesen Sie auf der nächsten Seite: Allen Regeln folgen? – Tools für die beschleunigte Zertifizierung

Seite 1 von 212