Statische Codeanalyse

Rettende Software

28. Mai 2015, 11:44 Uhr | von Dr. Paul Anderson

Rückrufe von medizinischen Geräten sind nicht ungewöhnlich. Einer der wichtigsten Gründe dafür ist fehlerhafte Software. Laut der US-amerikanischen Aufsichtsbehörde FDA beruhen vier von zehn Ausfällen von Medizingeräten auf einem Softwareproblem. Darum empfiehlt sie den Einsatz von statischen Analysetools, um Softwarefehler bereits in der Entwicklung auszumerzen.

Eines der fehleranfälligsten Geräte der heutigen medizinischen Ausrüstung findet den häufigsten Einsatz: Die Infusionspumpe ist das vorherrschende Instrument in der Patientenversorgung zur kontrollierten direkten Verabreichung von Flüssigkeiten (Nahrung, Medikamente) in den Körper von Patienten. Weil Infusionspumpen eine zentrale Rolle bei der Verabreichung von gefährlichen Flüssigkeiten einschließlich hochdosierter Medikationen spielen, können sich Fehlfunktionen für die Patienten gravierend auswirken. So registrierte die FDA von 2005 bis 2009 jährlich über 10 000 Beschwerden über Infusionspumpen.

Gerätebezogene Probleme führten zu einer großen Anzahl an ernsten Verletzungen und zu über 500 Todesfällen. Im selben Zeitrahmen riefen Pumpenhersteller mehr als 87 Geräte zurück – als Spitzenreiter bei Medizingeräten. Weil laut Untersuchungen der FDA zufolge viele Verletzungs- und Todesfälle auf Fehler beim Produktdesign und Entwicklung zurückzuführen sind, rief sie 2010 die »Infusion Pump Improvement Initiative« ins Leben. Sie sieht unter anderem verbesserte Softwareentwicklung und Verifikationsprozesse in der Branche vor und empfiehlt die statische Codeanalyse, um die gefährlichsten Fehlertypen und Sicherheitsrisiken aufzuspüren.

Prüfung aller möglichen Pfade

Bisher haben sich die Hersteller auf Tests und Code-Reviews auf Systemebene zur Verifizierung und Validierung ihrer Software verlassen. Die Laufzeittests prüfen allerdings nur einen kleinen Anteil der möglichen Codepfade. In der Vergangenheit konnte man die Tests mit einer manuellen Codeprüfung ergänzen, was deutlich mehr Pfade abdeckte. Aber die großen Codebasen von modernen Medizingeräten machen es unmöglich, die riesige Anzahl an Softwarepfaden zu analysieren. Gängige Verifizierungsmethoden finden einige Fehler, aber verifizieren die Zuverlässigkeit des übrigen Codes nicht. Die statische Codeanalyse prüft eine Anwendung, ohne dass sie die Software tatsächlich ausführen muss. Beispielsweise funktioniert das Analysetool »CodeSonar« ähnlich einem Compiler: Es nutzt Quellcode als Input, den es dann parst und in eine Zwischendarstellung (IR, Intermediate Representation) umwandelt. Während ein Compiler mit der IR Objektcode generieren würde, behält CodeSonar die IR und nutzt die Information, um das Programm abstrakt oder symbolisch auszuführen. Checker prüfen den Code, um unter aanderem gängige Fehler, Verletzungen von Richtlinien aufzuspüren, indem sie das Modell durchlaufen oder nach bestimmten Eigenschaften oder Mustern untersuchen, die auf Fehler hinweisen. Hoch entwickelte symbolische Ausführungstechniken durchsuchen Pfade mithilfe eines Kontrollflussdiagramms, einer Datenstruktur, welche die Reihenfolge darstellt, der Anweisungen während der Ausführung eines Programms folgen. Algorithmen verfolgen den abstrakten Zustand des Programms und können unmögliche Pfade ausschließen.

Hilfen bei sicherheitskritischem Programmieren

Sowohl die FDA als auch die EU verlangen einen dokumentierten Software-Entwicklungsprozess (beispielsweise nach der IEC 62304), obwohl die FDA nicht ausdrücklich auf Programmierstandards besteht. Dennoch haben noch viel zu wenige Hersteller von Medizingeräten die Bedeutung sicherheitskritischer Kodierungsstandards erkannt und sie eigenständig als Teil ihrer Qualitätsinitiativen übernommen. Gerade wenn es um Menschenleben geht, sollten sich Entwickler von Medizingeräte-Software aber zum Einsatz der besten Methoden verpflichten. Viele der Best Practises sind mit nur minimalem Aufwand und Kosten verbunden, und inzwischen existieren ausgezeichnete sicherheitskritische Standards für Medizintechnik – wie etwa die »Power of 10«-Regeln für sicherheitskritisches Programmieren. Diese zehn einfachen, leicht zu merkenden Programmierregeln sind so konzipiert, dass ein Missbrauch automatisch von statischen Analysetools entdeckt wird. Die moderne statische Quellcodeanalyse bietet den effektivsten Weg, um die wirksame Umsetzung eines Programmierstandards sicherzustellen. Zusätzlich zur Prüfung des Codes nach seiner Konformität mit Standards kann sie auch ernste semantische Fehler aufdecken, wie Buffer-Overruns, Nullzeiger-Dereferenzierungen, Race-Conditions und Ressourcen-Lecks oder eine fehlerhaft genutzte Schnittstelle zwischen Programmen (API).

Wie mit dem Risikofaktor Fremdcode umgehen?

Beinahe 30 Prozent des Codes in Medizingeräten besteht aus kommerzieller Fremdsoftware im Binärformat ohne eigentlichen Quellcode, zum Beiaspiel Grafiken und Windowing-Toolkits, Kryptografie-Bibliotheken, Middleware und Datenbanken. Besonders gefährdet sind Systeme, die sich aus Code von unterschiedlichen Anbietern zusammensetzen. Auch die zunehmende Vernetzung von Geräten bietet neue Angriffsflächen. Denn Fehler, die zu Sicherheitsschwachstellen führen, haben ihren Ursprung oft an den Schnittstellen zwischen Modulen und begründen sich auf harmlose Verständigungsprobleme, etwa wenn Spezifikationen unterschiedlich interpretiert werden. Sind Teile des Quellcodes nicht verfügbar, eignen sich Mischformen der statischen Analysetools, die Quell- mit Binärcodeanalyse kombinieren.

Zu den Best Practices von Entwicklern gehört, dass sie Eingaben aus potenziell gefährlichen Kanälen immer als risikoreich einstufen, bis deren Gültigkeit geprüft wurde. Ungeprüfte Eingabewerte heißen im Sprachgebrauch des sicheren Programmierens »tainted«. Zu den typischen Fehlerarten durch »tainted« Daten gehören neben Buffer-Overruns SQL- oder Befehl-Injektion, Cross-Site-Scripting, Arithmetic-Overflow und Path-Traversal. Zu prüfen, ob solche Daten korrekt verarbeitet werden, kann eine echte Herausforderung sein, denn in diesem Fall ist eine Überprüfung des Datenverlaufs durch die Codestruktur notwendig. Weil diese bereits bei relativ kleinen Programmen sehr aufwendig ist, lässt sie sich für die meisten Echtzeitanwendungen manuell nicht durchführen.

Als weitere Empfehlung sollte der Code nicht in einer Umgebung ausgeführt werden, in welcher der Angreifer keine Kontrolle über den Wert der Umgebungsvariablen hat und damit die Schwachstelle möglicherweise nicht ausnutzen kann. Solange der Code nicht korrigiert wird, bleibt er dennoch risikoreich und ein Haftungsproblem. Ein Entwickler könnte zudem auf die Idee kommen, den Code in einem anderen Programm wiederzuverwenden, in dem er nicht sicher wäre. Weil sich die manuelle Suche nach Programmierfehlern, die für »tainted« Werte anfällig sind, extrem zeitaufwendig gestaltet, liegt der beste Ansatz in deren Automatisierung.

Taint-Analyse unterstützt Entwickler

Bild 1: Beispiel einer Buffer-Overrun-Schwachstelle – die Unterstreichung stellt den Effekt von Taint dar
Bild 1: Beispiel einer Buffer-Overrun-Schwachstelle – die Unterstreichung stellt den Effekt von Taint dar

Die Taint-Analyse (Bild 1) zeigt Entwicklern einen möglichen Verlauf von riskanten Daten von einem Programmteil in einen anderen.

Moderne statische Analysetools unterstützen dabei, mögliche Angriffsflächen auf der Programmoberfläche besser zu verstehen, und können die Fehlersuche und Korrektur erheblich verkürzen.

Auch wenn manche Tools die Taint-Analyse dynamisch ausführen, bietet ein statischer Ablauf wichtige Vorteile – und stellt sicher, dass nicht nur solche Ausführungen möglich sind, für die Testfälle vorliegen, sondern alle (Bild 2).

 

 

Bild 2: Moderne statische Analysetools können mithilfe der Taint-Analyse den Fluss gefährlicher Daten über verschiedene Pfade durch das Programm visualisieren
Bild 2: Moderne statische Analysetools können mithilfe der Taint-Analyse den Fluss gefährlicher Daten über verschiedene Pfade durch das Programm visualisieren

Medizingeräte-Hersteller sollten sicherheitskritische Programmierstandards übernehmen müssen – schließlich geht es um das Wohl der Patienten.

Über den routinemäßigen Einsatz von modernen statischen Analysetools lassen sich Fehler aufdecken, die nur unter ungewöhnlichen Umständen und sehr früh in der Entwicklung auftreten.

Damit gewährleisten sie die korrekte Implementierung und Sicherheit von Software.

Über den Autor:

Dr. Paul Anderson ist Vice President Engineering bei GrammaTech.

Buffer-Overrun 
Der berüchtigte Buffer-Overrun hat in den letzten zwei Jahrzehnten einige der schlimmsten Cyberangriffe ausgelöst. Diese so weitverbreitete Schwachstelle zeigt die Wichtigkeit der Taint-Analyse gut auf: Nachfolgend wird der klassische Fall betrachtet, der es dem Hacker ermöglicht, den Prozess zu übernehmen und beliebigen Code auszuführen. Im aufgeführten Code ist der Buffer auf dem Stack:

void config(void)
{
   char buf[100];
   int count;
   …
   strcpy(buf, getenv(“CONFIG”));
   …
}

Die Eingabe kommt von außen über einen Aufruf an »getenv«, der den Wert der Umgebungsvariablen namens »CONFIG« abruft. Der Programmierer dieses Codes erwartete, dass der Wert der Umgebungsvariablen in den Buffer passt, aber das wird nirgendwo sichergestellt. Erhält der Angreifer die Kontrolle über den Wert dieser Umgebungsvariablen, dann löst die Zuteilung eines Wertes mit einer Länge von mehr als 100 einen Buffer-Overrun aus. Bei »buf« handelt es sich um eine automatische Variable, die auf dem Stack als Teil des Aktivierungssatzes für den Ablauf hinterlegt ist. Darum wird jedes Zeichen nach den ersten 100 auf die Teile des Programm-Stacks außerhalb der Grenze von buf geschrieben. Die Variable namens »count« darf überschrieben werden (abhängig davon, wie der Compiler den Platz auf dem Stack zuteilt). Wenn dem so ist, dann kontrolliert der Hacker den Wert dieser Variablen. Als echten Gewinn für den Angreifer beinhaltet der Stack die Adresse, zu der das Programm springt, sobald es den Vorgang abgeschlossen hat. Um diese Schwachstelle auszuschlachten, kann der Angreifer den Wert der Variablen auf einen speziell gestalteten String mit einer Return-Adresse seiner Wahl setzen. Sobald die CPU das Ende der Funktion erreicht, kehrt sie statt zur Adresse im Funktionsaufruf zu dieser Adresse zurück.

 

Lesen Sie mehr zum Thema


Jetzt kostenfreie Newsletter bestellen!

Weitere Artikel zu GrammaTEch

Weitere Artikel zu Verifysoft Technology

Weitere Artikel zu Medizinelektronik