MISRA C auf Legacy Code anwenden: Best Practices im Nachgang

| Autor / Redakteur: Mark Hermeling * / Sebastian Gerstl

Statische Analyse in CodeSonar: Die statische Code-Analyse integriert sich eng in das Build System und kann so die Einhaltung der Programmierstandards weitgehend automatisch überwachen.
Statische Analyse in CodeSonar: Die statische Code-Analyse integriert sich eng in das Build System und kann so die Einhaltung der Programmierstandards weitgehend automatisch überwachen. (Bild: GrammaTech)

MISRA C hat sich als Programmierstandard in der Automobilbranche bewährt. Auch andere Branchen können von der Richtlinien- und Regelsammlung profitieren, um Sicherheit und Zuverlässigkeit der Anwendungen zu steigern. Der Einstieg birgt jedoch ein paar Hindernisse, die man umschiffen muss. Eine wichtige Hilfe dabei ist die statische Code-Analyse.

Über 20 Milliarden Geräte werden bis zum Jahr 2020 weltweit im Internet der Dinge vernetzt sein, so die Prognose des Marktforschungsunternehmens Gartner. Den Kern des IoT bilden Embedded-Systeme. Hier erwartet Transparency Market Research bis Ende 2022 einen weltweiten Umsatz von über 233 Milliarden US-Dollar. Der Trend ist deutlich: Embedded-Systeme bilden in absehbarer Zeit das Rückgrat der Wirtschaft und auch des privaten Lebens.

Damit einher gehen hohe Ansprüche, was Zuverlässigkeit und Sicherheit solcher eingebetteter Systeme betrifft, denn Embedded-Systeme werden zunehmend kritisch. Nicht nur für die Geschäftsmodelle zahlreicher Branchen: In der Medizintechnik oder bei Aspekten der funktionalen Sicherheit stehen Gesundheit und Leben von Menschen auf dem Spiel, sollte ein Gerät fehlerhaft arbeiten. Gleichzeitig sind die Entwicklungsteams gefordert, ihre Produkte möglichst schnell marktreif zu entwickeln.

Beide Ansprüche unter einen Hut zu bekommen, erfordert ein striktes und stringentes Vorgehen in der Entwicklung, das zahlreiche Fehlerquellen von vorneherein zuverlässig ausschließt, ohne dabei die Agilität und Leistungsfähigkeit der Entwickler einzuschränken. Dafür lohnt sich ein Blick auf die Automobilindustrie: Hier ist der Bedarf nach bestmöglicher Zuverlässigkeit und hoher Sicherheit schon lange ein wichtiges Thema. Entsprechend hat diese Branche bereits frühzeitig damit begonnen, Best Practices für die Software-Entwicklung zu etablieren. Die Motor Industry Software Reliability Association (MISRA), ein Branchenkonsortium mit Sitz in Großbritannien, hat im Rahmen eines nationalen Forschungsprogramms für sicherheitskritische Systeme Rahmenwerke für C (MISRA C) und C++ (MISRA C++) erstellt und pflegt diese. Ein wichtiges Element bei den Standards ist die Anwendung der statischen Code-Analyse, um die Einhaltung der Vorgaben bereits frühzeitig in der Entwicklung zu ermöglichen.

Statisch überprüfbare Regeln für die Code-Analyse

Als Regelwerk für die im Embedded-Bereich meistgenutzte Sprache (C) umfasst MISRA C in der Version 2012 insgesamt 143 Regeln, die mittels statischer Code-Analyse überprüft werden können. Dabei handelt es sich um in C zulässige Programmierpraktiken, die aber hinsichtlich Sicherheit und Zuverlässigkeit bedenklich sind.

Zudem stellt MISRA C 16 Direktiven auf, die sich mit dem Entwicklungsprozess und den Entwicklungsrichtlinien befassen. Damit ist es nicht auf den Automotive-Bereich beschränkt: Sie kann als generischer Rahmen in allen Branchen eingesetzt werden, um die Zuverlässigkeit einer Anwendung bereits auf dieser Ebene signifikant zu steigern. Zudem verbessert MISRA C die Wartbarkeit, da die Einhaltung der aufgestellten Regeln den Code übersichtlicher macht.

Dabei spielt die statische Code-Analyse eine entscheidende Rolle: Hierbei wird der Code im Gegensatz zum Testing nicht ausgeführt, sondern – ähnlich wie bei einem Compiler – in eine Intermediate Representation (IR) überführt. Das Analyse-Tool überprüft anhand der IR alle Daten- und Kontrollflüsse des Codes mit Hilfe von definierten Checkern. So können Programmierfehler erkannt werden, die häufig zu Sicherheitsproblemen führen. Dazu zählen unter anderem Buffer Overruns, Null-Pointer Dereferenzierungen oder Zugriffe auf potenziell unsichere Datenquellen. Dazu muss kein lauffähiger Code vorliegen, die statische Analyse kann also bereits in einer sehr frühen Phase der Entwicklung als integraler Bestandteil der Qualitätssicherung implementiert werden. Zudem lässt sich dieses Verfahren sehr gut automatisieren und in agile Ansätze wie Continuous Integration einfügen.

MISRA C und Überprüfbarkeit: Manche Regeln sind komplex

Für MISRA C ist die statische Überprüfbarkeit beim Erkennen von Abweichungen zentral. Einfachere Regeln können bereits mit den integrierten Analysefähigkeiten vieler Compiler überwacht werden. Regel 15.1 besagt z.B., dass der Befehl goto nicht genutzt werden soll. Ob der Befehl im Code auftritt, kann über eine einfache Syntaxprüfung ermittelt werden. Dazu muss das Analyse-Tool nur die Syntax der Compilation Unit parsen.

Etwas komplexer ist etwa die Regel 5.2., wonach Bezeichner, die innerhalb des gleichen Geltungsbereichs und Namensraums deklariert wurden, eindeutig sein müssen. Auch dies ist eine relativ einfache syntaktische Regel, jedoch muss ein Analyse-Tool zur Überprüfung über die Symboltabelle der Compilation Unit verfügen, um die Bezeichner und deren Geltungsbereich zu ermitteln.

Nur professionelle Analyse-Tools kommen mit den komplexeren, da nicht rein auf syntaktische Elemente fokussierten Regeln zurecht. Ein typisches Beispiel dafür ist Regel 2.2, die so genannten Dead Code verbietet. Unter totem Code versteht man jegliche Operation, deren Ergebnis das Verhalten eines Programms nicht beeinflusst – im Gegensatz zu unerreichbarem Code, der durch keinen möglichen Kontrollfluss erreichbar ist und deswegen das Verhalten eines Programms nicht beeinflusst. Um toten Code aufzuspüren, muss das Analyse-Tool die Semantik aller möglichen Ausführungen des Programms verstehen.

Kein lauffähiger Code notwendig: 
Die statische Code-Analyse nutzt eine Intermediate Representation als Modell zur Untersuchung.
Kein lauffähiger Code notwendig: 
Die statische Code-Analyse nutzt eine Intermediate Representation als Modell zur Untersuchung. (Bild: GrammaTech)

Die Einführung des MISRA-Rahmenwerks erfordert jedoch einen gewissen Aufwand. Dabei sind zwei Szenarien zu unterscheiden: Die Implementierung von MISRA C in neue Projekte und das Refactoring bestehender Anwendungen anhand des Standards. Die Anwendung von MISRA C auf neue Projekte ist etwas einfacher als das Refactoring von Legacy-Code. Hier kommt es zunächst darauf an, ob eine MISRA-Zertifizierung angestrebt wird oder nicht. Ist das nicht der Fall, muss auch nicht der komplette Standard umgesetzt werden. Es reicht, sich vor Projektbeginn diejenigen Regeln rauszusuchen, die für das Projekt besonders wichtig sind. Ein schmales, aber an den bestehenden Herausforderungen ausgerichtetes Subset macht die MISRA-Einführung deutlich überschaubarer und nicht zuletzt wirtschaftlicher. Ist der anzuwendende Regelsatz bestimmt, muss das Tool zur statischen Analyse entsprechend konfiguriert werden. Anwender professioneller Tools sind hier etwas im Vorteil, denn Produkte wie zum Beispiel CodeSonar von GrammaTech verfügen von Haus aus über Checker für den MISRA-Standard.

Neue Fehler vermeiden ist schwerer als man meint

Die größte Herausforderung ist die Interpretation der Reports, die das Analyse-Tool erzeugt. Hier ist zu Beginn von einer immensen Menge an Warnungen auszugehen, bis die Regeln innerhalb des Entwicklungsteams wirklich verankert sind. So gilt es, die relevanten und wichtigen Hinweise zu Verletzungen des Standards herauszufiltern. Auch hier haben die professionellen Tools wieder die Nase vorn, sie bieten in der Regel sehr umfassende Möglichkeiten, die Berichte zu organisieren und aufzubereiten. Dabei ist es auch hilfreich, Meldungen bei den täglichen Integrations bevorzugt zu behandeln. Warnungen aus älteren Code-Teilen, etwa wiederverwerteter Code aus früheren Projekten, können zunächst zurückgestellt und zu gegebener Zeit berücksichtigt werden.

Die Anwendung von MISRA C beim Refactoring älterer Anwendungen stellt ein paar zusätzliche Anforderungen. Hier sollte man zunächst festgelegen, welches Ziel damit erreicht werden soll. Das kann etwa eine höhere Sicherheit sein, eine geringere Fehlerquote für ausgelieferte Produkte, die Compliance zu einem bestimmten Standard oder dergleichen mehr. Ohne klare Ziele ist der Erfolg des Refactorings nicht messbar.

Daneben muss die Ausgangslage erfasst werden. Eine erste statische Analyse des Quellcodes mit den MISRA-Checkern ergibt den Baseline-Report, also einen Bericht mit den vor Projektbeginn vorhandenen Abweichungen vom Standard als künftige Referenz. Dieser ist erfahrungsgemäß extrem umfangreich. Dennoch sollte er genau ausgewertet werden, um die Regelverletzungen zu priorisieren und dann auch entsprechend zu adressieren. Grundsätzlich besteht die Gefahr, dass das Refactoring selbst wieder Abweichungen vom gewünschten Standard erzeugt. Um das zu vermeiden, bieten sich regelmäßige Vergleiche des aktuellen Berichts mit dem zu Beginn erzeugten Baseline-Report an. Neue Fehler zeigen sich hier sehr deutlich. Bei jeder Iteration sollten dann die neuen Fehler priorisiert behoben werden.

Fazit: Legacy Code durch Refactoring verbessern

MISRA C hat sich in der Automobilbranche bewährt. Da das Regelwerk grundsätzlich generisch angelegt ist, kann es aber in allen Bereichen der Embedded-Entwicklung zum Einsatz kommen. Dadurch, dass alle Regeln mit Hilfe der statischen Code-Analyse überprüfbar sein müssen, lässt sich dieser Ansatz gut in Projekte integrieren und die Überwachung in weiten Teilen automatisieren.

Davon profitieren nicht nur neue Projekte. Gerade Legacy-Code kann von einem Refactoring nach den MISRA-Standards enorm profitieren. Durch die strikten Vorgaben werden nicht nur Fehler und Sicherheitsrisiken beseitigt, sondern auch die Übersichtlichkeit insgesamt verbessert. Künftige Entwicklungsarbeit und Wartung binden dadurch weniger Ressourcen. Und auch die Wiederverwertung des Altcodes fällt deutlich leichter – besonders, wenn neue Projekte auch den MISRA-Regeln folgen.

Alptraum Legacy Code – Wie Profis damit umgehen

Alptraum Legacy Code – Wie Profis damit umgehen

18.02.19 - Legacy Code bringt oft gewaltige Probleme bei der Entwicklung neuer Features mit: Variablen tragen nichtssagende Namen, Methoden sind zu überkomplexen Konstrukten mutiert und automatisierte Tests sind meist wenig bis gar nicht vorhanden. Wie geht man damit um? lesen

Dieser Beitrag ist erschienen im Sonderheft Embedded Systems Development und Internet of Things I der ELEKTRONIKPRAXIS (Download PDF)

* Mark Hermeling ist Senior Director Product Marketing bei GrammaTech, Inc.

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Zur Wahrung unserer Interessen speichern wir zusätzlich zu den o.g. Informationen die IP-Adresse. Dies dient ausschließlich dem Zweck, dass Sie als Urheber des Kommentars identifiziert werden können. Rechtliche Grundlage ist die Wahrung berechtigter Interessen gem. Art 6 Abs 1 lit. f) DSGVO.
Kommentar abschicken
copyright

Dieser Beitrag ist urheberrechtlich geschützt. Sie wollen ihn für Ihre Zwecke verwenden? Kontaktieren Sie uns über: support.vogel.de/ (ID: 46294133 / Tools & Softwarekomponenten)