Suchen

Ist ein „Let it Crash”-Paradigma in C++ sicher?

| Autor / Redakteur: Christoph Woskowski * / Martina Hafner

„Lass es abstürzen“ (Let it Crash) ist selbstverständlich keine valide Fehlerbehandlungsoption für ein sicherheitskritisches System. Denn niemand mit Verantwortungsbewusstsein möchte, dass ein System als Ganzes zusammenbricht. Aber wie sieht es mit seinen Komponenten aus?

Firma zum Thema

Das Zusammenspiel von Robustheit und funktionaler Sicherheit (Safety) ist ein äußerst komplexes Thema.
Das Zusammenspiel von Robustheit und funktionaler Sicherheit (Safety) ist ein äußerst komplexes Thema.
(Bild: gemeinfrei / CC0 )

Unter Umständen wäre es von Vorteil, wenn eine fehlerhafte bzw. „verrückt spielende“ Kontrolleinheit oder Software-Komponente komplett ausfallen würde – um sofort gegen ein redundantes (oder besser diversitäres) Element ausgetauscht zu werden – anstatt das Gesamtsystem über einen längeren Zeitraum zu kompromittieren.

Das Robustheits-Paradigma „Let it Crash“ (LiC) der Programmiersprache Erlang ist ein etwas anderer Ansatz, um Fehler zu isolieren und zu behandeln. Sehr leichtgewichtige Prozesse im Zusammenspiel mit nachrichtenbasierter Komponentenkommunikation ermöglichen ein klares und einfaches Nebenläufigkeitsmodell. Prozesse können einander überwachen und sich – im Fall einer Prozess-Terminierung – sehr schnell gegenseitig neu starten.

Bildergalerie

Die bevorzugte Fehlerreaktion für einen Worker-Prozess ist daher, sich selbst zu beenden, falls er eine Situation nicht lokal bewältigen kann. Supervisor-Hierarchien stellen eine angemessene Fehlerbehandlung sicher, indem sie entweder einen alternativen Worker-Prozess starten oder einen erneuten Versuch mit einer neuen Instanz des Ursprungsprozesses wagen.

Ein nicht ganz unwichtiger Nebeneffekt ist die Möglichkeit, die fachlichen Aspekte – realisiert durch Hierarchien von Worker-Prozessen – im Sinne von „Separation of Concerns“ komplett vom Error-Handling durch Supervisor-Hierarchien zu trennen.

Robustheit versus Sicherheit

Aber was macht das Zusammenspiel von Robustheit und funktionaler Sicherheit (Safety) so komplex? Die beiden Ziele scheinen nur schwer miteinander vereinbar zu sein. Wenn die Sicherheit von Mensch und Umwelt auf dem Spiel steht, geht es um sehr schnelle Reaktionen auf kritische Ereignisse, wie beispielsweise Hardware-Ausfälle.

Im Fall eines Infusionssystems im Operationssaal, ist eine solche Reaktion ein sofortiger Stopp jeglicher Förderaktivität und Start der Alarmierung. Gegenüber Ausfällen kritischer Komponenten ist ein solches System bewusst nicht robust. Was im Notfall richtig und wichtig ist, führt jedoch bei gehäuften Fehlalarmen (z.B. durch Programmierfehler oder Hardwaretoleranzen) zur Frustration beim Anwender.

Ein perfekt robustes System auf der anderen Seite würde praktisch nie alarmieren. Im Extremfall würde es jedoch zu lange versuchen, einen Teilausfall zu kompensieren und so ggf. eine sicherheitskritische Reaktion verhindern oder zumindest verzögern.

Ist es also überhaupt sinnvoll, ein Robustheits-Paradigma wie Erlangs „Let it Crash“ in einer sicherheitskritischen Anwendung einzusetzen? Folgende Machbarkeitsstudie gibt darauf eine Antwort.

Szenario der Machbarkeitsstudie

Den technischen Hintergrund bildet ein fiktives Telemonitoring-System für die Überwachung und Betreuung von Patienten in der eigenen Wohnung. Durch das Tragen so genannter „funktionaler Kleidung“ – aus Textilien, in welche kabellose Sensoren integriert sind – können Patienten überwacht werden, ohne durch Kabel oder Sonden in ihrer Bewegungsfreiheit eingeschränkt zu sein.

Jeder Sensortyp misst entweder Temperatur, Herzschlag oder Atemfrequenz und ist jeweils mehrfach vorhanden. So können Ausfälle und Messfehler, zum Beispiel durch eine ungünstige Messposition, kompensiert werden. Um den Energieverbrauch zu reduzieren, sollte zu jedem Zeitpunkt trotzdem nur ein Sensor eines Typs aktiv sein, der Rest befindet sich auf Abruf im stromsparenden Standby.

Mittels Kurzstreckenfunk im ISM-Band (z.B. IEEE 802.15.4) werden die Messwerte an eine Basisstation in der Wohnung des Patienten übertragen. Dieses Gerät hält je eine Verbindung zu den derzeit aktiven Sensoren, kann aber auch andere aufwecken oder in den Standby versetzen und so auf alternative Messpositionen wechseln, falls nötig (Sensor-Ausfall, implausible Daten).

Gleichzeitig hält die Basisstation eine ständige Verbindung zu einem Krankenhaus und transferiert die aktuellen Vitalwerte für eine automatisierte Auswertung oder Beurteilung durch Fachpersonal. Als redundante Maßnahme, zusätzlich zum Krankenhaussystem, ist das Heimgerät in eingeschränktem Maße fähig, schwere Messfehler sowie kritische Situationen zu erkennen. In diesem Fall alarmiert die Basisstation audiovisuell, benachrichtigt das Krankenhaus und sendet eine SMS oder Audionachricht an eine konfigurierbare Telefonnummer.

Implementierung in Erlang

Eine prototypische Implementierung des beschriebenen fiktiven Szenarios als eine in Erlang geschriebene PC-Anwendung ermöglicht den Test verschiedener Varianten von Worker- und Supervisor-Prozesshierarchien. Zusätzlich kann die angestrebte Trennung von Businesslogik und Fehlerbehandlung evaluiert werden.

Die Umsetzung konzentriert sich auf die Software der „Basisstation“ und simuliert die externen Sensoren lediglich. Auch die Anbindung an das Krankenhaussystem wird nur angedeutet. Die implementierte, generische Supervisor-Hierarchie ist ausschließlich dafür verantwortlich, Worker-Prozesse wie Sensortreiber und Datenkollektor initial zu starten und Fehler zu behandeln – indem ausgefallene Prozesse neu gestartet oder ersetzt werden.

Sensortreiber und Datenkollektor auf der anderen Seite beinhalten ausschließlich die Kernfunktionen (Businesslogik) und dafür keine Fehlerbehandlung. So beendet sich ein Sensortreiber einfach selbst, sobald er die Verbindung zum Sensor verliert, und wird durch einen anderen ersetzt. Gleiches passiert, wenn zwar Daten vorhanden aber außerhalb plausibler physikalischer bzw. physiologischer Grenzen sind.

Und in C++?

Es existieren bereits seit längerem sehr mächtige Actor-Frameworks für C++ (z.B. www.state-machine.com oder www.actor-framework.org), die auf leichtgewichtige aktive Objekte und nachrichtenbasierte Kommunikation setzen. Doch spätestens seit C++11 gibt uns auch die Programmiersprache selbst (fast) alle Zutaten in die Hand, um einen „Let it Crash“-ähnlichen Programmierstil mit „Hausmitteln“ zu entwickeln:

  • Vergleichbar mit Erlang-Prozessen in punkto Leichtgewichtigkeit und guter Verwendbarkeit sind die seit C++11 verfügbaren plattformunabhängigen Standard-Threads (std::thread).
  • Nebenläufigkeit im Zusammenspiel mit geteilten Daten erfordert einen aufwendigen und oft fehleranfälligen Schutz dieser Daten (z.B. via Mutex). Während Erlang-Prozesse dieses Problem umgehen, indem sie als echte Prozesse grundsätzlich keine Daten teilen, können zwischen Threads geteilte Daten im Fall von C++ nur per Konvention „verhindert“ werden.
  • Auch ohne Daten zu teilen, können Erlang-Prozesse gemeinsam auf einer Datenbasis arbeiten. Per Messaging werden die benötigten Informationsabschnitte verteilt, sowie die Ergebnisse eingesammelt und zentral zusammengeführt. In seinem Buch „C++ Concurrency in Action“ stellt Anthony Williams ein einfaches Message-Passing vor, welches gleiches auch für C++ Standard-Threads möglich macht.
  • Wenn ein Erlang-Prozess sich selbst beendet bzw. beendet wird, bekommt ein mit diesem Prozess verlinkter Supervisor eine Nachricht inkl. dem Grund für das Ende des Prozesses. In C++ lässt sich ein Thread via Exception terminieren. Seit C++11 können aufgefangene Exceptions via Exception-Pointer an einen anderen Thread weitergegeben und dort erneut geworfen werden.

Die aus diesen Zutaten entwickelte kleine Header-Only-Bibliothek „LiC++“ ermöglicht die Umsetzung des oben beschriebenen Telemonitoring-Szenarios in C++.

LiC++ Beispiel

Zum Abschluss soll ein kleines Beispiel zeigen, wie C++ „Let it Crash“ aussehen kann. Das zugehörige (und nicht ganz ernst gemeinte) Erlang-Programm stammt aus dem Bruce Tate Buch „Seven Languages in Seven Weeks“. Zum Vergleich kann der Erlang-Code unter der Adresse www.pragprog.com heruntergeladen werden.

Bildergalerie

Inhaltlich macht der Code folgendes: Ein Worker-Thread „Roulette“ empfängt vom Anwender Zahlen im Bereich von eins bis sechs. Alle Zahlen außer drei führen zu einem „click“. Die Zahl drei beendet den Thread mit einem Knall („bang“). Der Supervisor-Thread „Doctor“ startet und überwacht den „Roulette“-Thread. Wird „Roulette“ beendet, startet der „Doctor“ eine neue Instanz und das Spiel geht weiter.

Zusammenfassung

Es lässt sich zeigen, dass durchaus Szenarien denkbar sind, die ein Robustheits-Paradigma wie „Let it Crash“ in sicherheitskritischen Anwendungen sinnvoll erscheinen lassen. Zudem wird deutlich, dass ein von der Erlang-Community schon länger propagierter, nebenläufigkeitsfreundlicher Programmierstil (spätestens seit C++11) auch in Industriebereichen möglich ist, die hauptsächlich auf C++ setzen.

Es lohnt sich daher, öfter über den Tellerrand schauen, um bewährte Lösungen aus anderen Entwicklungsumgebungen und Sprachen zu evaluieren und ggf. zu adaptieren. Das Beispiel „Let it Crash“ zeigt, dass der Phantasie hier wenige Grenzen gesetzt sind.

* Christoph Woskowski ist Lead Software Architect für Embedded Systeme bei Zühlke und seit mehr als 7 Jahren im Unternehmen. Er verfügt über eine breite Erfahrung in der Konzeption und Umsetzung von Gerätesoftware. Er hat über viele Jahre in unterschiedlichen Rollen sicherheitskritische Projekte als Software- und Systemarchitekt begleitet.

(ID:44601223)