Software-Test Wie robust ist mein Code?

Standards zur Entwicklung sicherheitskritischer Software fordern oftmals Tests unter abnormalen Bedingungen und mit ungültigen Eingaben. Damit soll untersucht werden, wie ein Testobjekt damit zurechtkommt. Im Folgenden zeigen wir, worauf es bei Fehler-injektion und Robustheitstest ankommt.

Einschlägige Normen und Standards zur Entwicklung sicherheitskritischer Software verlangen oft die Fehlerinjektion (Fault Injection) als Testmethode, beispielsweise die ISO 26262 in Teil 6, Abschnitt 9, Tabelle 10 für den Software-Unit-Test. Mit dieser Methode soll geprüft werden, ob eine Software-Unit, beispielsweise eine Funktion im Sinne von C, mit einem von außerhalb der Unit herrührenden Fehler zurechtkommt. Die ISO 26262 definiert in Abschnitt 1.42 von Teil 1 (Vokabular) einen solchen Fehler als abnormale Bedingung, die zum Versagen beispielsweise einer Software-Unit führen kann. Andere Standards wie IEC 61508 oder IEC 62304 fordern vergleichbare Maßnahmen.

Fehlerinjektion steht in engem Bezug zum Robustheitstest. Unter Robustheit von Software wird im Allgemeinen die Fähigkeit verstanden, auf ungültige Eingaben und Bedingungen sinnvoll zu reagieren. Ungültige Eingaben und Bedingungen sind überaus vielfältig. Es kann beispielsweise im Rahmen des Systemtests das Senden einer CAN-Nachricht mit ungültiger Nutzlast an das ABS-System eines Automobils sein. Es kann aber auch im Rahmen des Software-Unit-Tests die Übergabe eines NULL-Zeigers an die Software-Unit sein.

Teilweise sind die abnormalen Bedingungen und die Reaktion darauf genauer spezifiziert, beispielsweise könnte der Übergabe eines NULL-Zeigers an eine Software-Unit als Reaktion der Return-Wert von –1 und eine bestimmte Fehlernummer in einer globalen Variablen zugeordnet sein. Hier kann man die Injektion einer abnormalen Bedingung eigentlich als anforderungsbasierten Test betrachten. Manchmal sind die abnormalen Bedingungen nicht näher bezeichnet, deshalb bleiben eigentlich nur Raten (Error Guessing) oder zufällige Erzeugung der Testeingangsdaten als Methoden zur Erzeugung von Testfällen. Gibt es keine spezifizierten Eingabebedingungen, gibt es auch keine spezifizierten Reaktionen darauf. Man kann nur schwerwiegendes Fehlverhalten erkennen, beispielsweise eine verspätete Antwort oder überhaupt keine Antwort. Tests dieser Art fallen unter die Rubrik Robustheitstest.

Unspezifizierte Fehlerinjektion

Fuzzing ist eine Technik, um an Testeingangsdaten für Robustheitstests zu kommen. Hierbei werden Daten automatisch nach bestimmten Vorgaben und Regeln erzeugt. Ganz ohne Vorgaben geht es normalerweise nicht, dann ist beispielsweise eine Nachricht an ein Testobjekt zu schicken, dann muss das Nachrichtenprotokoll zumindest grundlegend eingehalten werden, um die Nachricht überhaupt empfangen zu können. Ansonsten ist die dem Fuzzing zugrunde liegende Idee, Eingaben mit allen möglichen Kombinationen von Werten zu erzeugen und diese als Testfälle zu verwenden. Dies kann bisher unbekanntes Fehlverhalten der verarbeitenden Software aufdecken (Zero-Day Vulnerability). Anstelle des Ansatzes, alle Kombinationen mehr oder weniger der Reihe nach auszuprobieren, können auch genetische Algorithmen angewendet werden, was einen effizienteren Test verspricht.

Um Fehlverhalten der Software festzustellen, ist das Testobjekt zu überwachen, was üblicherweise ein Monitor übernimmt. Abhängig von den technischen Gegebenheiten lässt sich mehr oder weniger präzise feststellen, ob ein Fehlverhalten des Testobjekts vorliegt und was dazu geführt hat. Ist der Einblick ins Innere des Testobjekts nicht möglich, muss man sich mit nach außen sichtbaren Reaktionen begnügen. Schickt beispielsweise ein Testobjekt in regelmäßigen Abständen eine Nachricht als Lebenszeichen, so muss der (externe) Monitor dies überwachen. Bleibt die Nachricht aus, weiß man, dass ein Fehlverhalten aufgetreten ist, kann dies aber nicht weiter eingrenzen. Lässt sich der innere Zustand des Testobjekts überwachen, kann man unter Umständen die Ursache eines Fehlverhaltens (z. B. Segmentation-Fehler) ermitteln. Wichtig ist in jedem Fall, die Eingabe, die zum Fehlverhalten geführt hat, zu protokollieren, um das Fehlverhalten reproduzieren zu können. Damit lässt sich dann der Defekt im Testobjekt beheben.

Bild 1 beschreibt den Ablauf eines Robustheitstests mittels Fuzzing. Zuerst ist das Nachrichtenformat zu ermitteln. Dann werden (zufällige) Nachrichten in diesem Format erzeugt und an das Testobjekt gesendet. Solange das Testobjekt normal reagiert (beispielsweise nicht abstürzt) werden weitere Nachrichten erzeugt und versandt. Ansonsten protokolliert der Monitor jene Nachricht, um das Fehlverhalten reproduzieren zu können. Ein solches Werkzeug zum Robustheitstest ist beSTORM. Es sendet Nachrichten an ein Testobjekt, wobei es eine Vielzahl von Protokollen unterstützt. Es benutzt Fuzzing, um die Nachrichteninhalte automatisch zu erzeugen.