Unübersichtliche Regelungen erschweren die Überprüfung und Einhaltung von Richtlinien. Die striktere und einfach zu verstehende Alternative hier: Die Straße gar nicht befahren.

Unübersichtliche Regelungen erschweren die Überprüfung und Einhaltung von Richtlinien. Die striktere und einfach zu verstehende Alternative hier: Die Straße gar nicht befahren. (Bild: AdobeStock / photocrew)

Typische Regelwerke im Bereich von eingebetteten Systemen, wie MISRA, CERT oder die ISO/IEC 17961 (C Secure Coding), sind nicht vollständig automatisch überprüfbar. Dies kann mehrere Gründe haben: So können Regeln beispielsweise „unentscheidbar“, d. h. nur mit eingeschränkter Präzision durch statische Codeanalysewerkzeuge überprüfbar sein. Bekannte Beispiele sind die Erkennung möglicher Dereferenzierungen von NULL-Pointern oder das Auffinden möglicher Divisionen durch 0. In beiden Fällen muss durch ein Analysewerkzeug die Menge möglicher Werte an einer bestimmten Stelle im Code ermittelt werden.

Was für einfache Beispiele noch problemlos möglich erscheint, ist für komplexe Embedded-Systeme im Allgemeinen nicht mehr absolut präzise möglich. Analysewerkzeuge müssen in diesen Fällen Abschätzungen verwenden und Heuristiken einsetzen. Als Konsequenz kommt es zu „false positives“, also Meldungen, die einen Fehler anmerken, der tatsächlich nicht existiert, dessen Abwesenheit aber nicht automatisiert nachgewiesen werden konnte.

Andere Regeln sind möglicherweise sogar als „manuell“ eingestuft, also für eine automatisierte Prüfung generell unzugänglich. Zuletzt ist die Verfügbarkeit von Regeln durch die eingesetzten Tools begrenzt. Natürlich versuchen die Hersteller statischer Analysewerkzeuge eine möglichst hohe Abdeckung der Regelsätze zu gewährleisten. Dennoch entwickeln sich auch die Regelsätze weiter, es werden neue Regeln hinzugefügt und bestehende Regeln inhaltlich ergänzt, ersetzt oder gleich ganz entfernt. Daher kann es zu Abweichungen zwischen den für ein Projekt erwünschten Regeln und den in einem konkreten Werkzeug zur Verfügung stehenden Regeln kommen.

Doch auch in diesen Fällen können statische Analysewerkzeuge gewinnbringend eingesetzt werden. So ist es abhängig von Projekt und Kontext denkbar, weniger präzise Regeln zu verwenden und dennoch die Ziele eines Regelwerks zu erreichen. In anderen Fällen können Analysewerkzeuge manuelle Reviews unterstützen, indem sie kritische Stellen markieren und so ein zielgerichteteres, aber trotzdem manuelles Vorgehen ermöglichen.

Statische Codeanalyse

Statische Codeanalyse ist ein Oberbegriff für verschiedene Techniken und Ansätze zur Analyse von Quellcode. Allen Techniken ist gemeinsam, dass sie die tatsächliche Ausführung des Codes vermeiden. Hier unterscheidet sich statische Analyse grundsätzlich von anderen Techniken wie Testing oder Model Checking. Statt den Code auszuführen, werden die Eigenschaften von Interesse direkt aus dem Quellcode oder einer geeigneten Zwischendarstellung extrahiert. Statische Codeanalyse verwendet dazu verschiedene Techniken:

  • Style Checking oder Linting durchsucht den Sourcecode nach verbotenen Konstrukten.
  • Datenfluss- und Zeigeranalysen, bei denen die Analysewerkzeuge versuchen, die möglichen Werte von Variablen und die möglichen Ziele von Pointern an bestimmten Stellen im Code zu inferieren. Diese Informationen können dann genutzt werden, um etwa Dereferenzierungen invalider Pointer zu erkennen.
  • Abstrakte Interpretation, bei der das Verhalten des Quellcodes abstrahiert und überabgeschätzt wird. Dazu wird eine abstraktere Version der Instruktionen und eine abstraktere Version der tatsächlichen Daten verwendet. Beispielsweise könnte ein abstrakter Interpreter statt konkreter Integerwerte nur noch speichern, ob eine Zahl negativ, 0 oder positiv ist. Der Effekt einer Addition würde dann entsprechend durch diese Elemente ausgedrückt.
  • Symbolische Analysetechniken, bei denen die Instruktionen im Quellcode in Form mathematischer Formeln ausgedrückt werden, die dann durch die Analysesysteme gelöst werden. Abhängig von der Codierung bedeutet eine Lösbarkeit oder Nicht-Lösbarkeit einer Formel dann, dass der Quellcode eine bestimmte Eigenschaft aufweist.
  • Kombinationen aus diesen Techniken und weiteren Analysen.
Werkzeuge zur statischen Codeanalyse prüfen auch große und komplexe Systeme automatisch und bereiten die Ergebnisse für die Weiterentwicklung und Verbesserung auf.
Werkzeuge zur statischen Codeanalyse prüfen auch große und komplexe Systeme automatisch und bereiten die Ergebnisse für die Weiterentwicklung und Verbesserung auf. (Bild: Axivion)

Statistische Codeanalyse

Statische Codeanalysetools bieten eine hohe Abdeckung von Regelsätzen bei gleichzeitig hoher Präzision. Schwachstellen und lückenhafter Abdeckung von Regelsätzen kann dabei auf verschiedenen Arten begegnet werden. Das macht die Analysewerkzeuge auch jenseits der klassischen Regelprüfung zu einer wertvollen Unterstützung im Entwicklungsprozess.

Die wichtigsten Abkürzungen im Bereich Elektronik und Allgemeines

Dieses Abkürzungsverzeichnis erklärt eine Fülle von Abkürzungen aus dem Bereich Elektronik und begleitenden Randbereichen. Dabei geht es primär um physikalische Größen, Funktionen, Normen, Organisationen und typische Elektronik- oder Automotive-Fachbegriffe.

Dank der erwähnten Techniken sind Tools zur statischen Codeanalyse in der Lage, auch große und komplexe Systeme zu analysieren. Das hat aber einen Preis: Mit steigender Abstraktion vom Quellcode und zur tatsächlichen Ausführung des Codes kommt es zu abnehmender Präzision. Das führt dazu, dass statische Analysesysteme abhängig von ihrer Konfiguration möglicherweise „false positives“ oder „false negatives“ melden.

Striktere, aber prüfbare Regeln

Um Regeln zu umschiffen, die nur schwierig oder unpräzise prüfbar sind, könnten Regeln verwendet werden, die leichter zu überprüfen sind. Hier ist die Idee, meist nicht zu checken, ob ein Fehler tatsächlich auftritt. Sondern zu überprüfen, ob überhaupt die Voraussetzungen für einen möglichen Fehler gegeben sind.

Dazu ein Beispiel: Angenommen eine Regel fordert, dass bei einem Typecast einer numerischen Variablen kein Werteverlust auftritt. Um diese Regel präzise zu prüfen, wäre es notwendig, die Wertebereiche aller Zahlen im Projekt zu kennen und zu verfolgen, um so bei etwaigen Casts entscheiden zu können, ob aktueller Wert und neuer Typ zusammenpassen. Das ist im Allgemeinen jedoch unentscheidbar und nur ineffizient möglich.

Hier kommen mehrere striktere Regeln infrage, die leichter prüfbar sind, aber insgesamt ein äquivalentes Ergebnis liefern:

  1. Es dürfen nur (explizite) Casts von einem Typ mit geringerem Wertebereich in einen Typ mit größerem Wertebereich vorgenommen werden. Hier kann statisch und präzise anhand der Typen entschieden werden, ob nur sichere Casts verwendet werden.
  2. Casts zwischen numerischen Typen werden generell untersagt. Besonders einfach zu prüfen, aber für viele Projekte schon zu strikt.
  3. Ein Mischbetrieb, in dem etwa für Integers tatsächlich Wertebereiche ermittelt werden, bei Gleitkommazahlen aber eine der zuvor beschriebenen Varianten zum Einsatz kommt. Dies reduziert die Unentscheidbarkeit nur zum Teil, erleichtert aber bereits die Implementation.

Alle drei Varianten sind signifikant einfacher zu prüfen als ursprüngliche Regel. Abhängig vom konkreten Projekt und dessen Anforderungen können alle Varianten als maßgeschneiderte Regeln realisiert werden und liefern die gleiche Sicherheit wie die ursprüngliche Anforderung.

Regeln mit eingeschränktem Scope

Neben einer Änderung der Anforderung wie im ersten Ansatz beschrieben, ist ebenfalls eine Änderung des Scopes einer Regel denkbar. Toolhersteller liefern dabei typischerweise nur Regeln aus, die sie relativ vollständig in allen Situationen prüfen können. Konkrete Softwareprojekte befinden sich aber nie in allen diesen Situationen gleichzeitig. Wenn eine Regel beispielsweise einen Fehlerfall beschreibt, der in POSIX- und Nicht-POSIX-Umgebungen auf unterschiedliche Art und Weise auftreten kann, so muss eine sinnvolle automatisierte Prüfung beide Fälle abdecken.

Ein konkretes Projekt wird in vielen Fällen jedoch nicht allen Umgebungen und Varianten verwendet. Zur Prüfung kann es also ausreichen, sich auf einen bestimmten Aspekt oder eine bestimmte Umgebung zu konzentrieren und beispielsweise nur den POSIX-Anteil einer Prüfung zu automatisieren. Hier kommt es natürlich darauf an, ob die vom konkreten Projekt verwendete Umgebung tatsächlich eine vereinfachte Prüfung ermöglicht oder den komplexen Teil der Regel darstellt.

Review Support

Anforderungen, die nicht automatisiert überprüft werden können, müssen üblicherweise durch manuelle Reviews sichergestellt werden. Das heißt im Fall nicht-automatisierbarer Regeln, dass der Quellcode periodisch oder fortlaufend geprüft wird und entsprechende Nachweise der erfolgten Überprüfung erstellt werden.

Dies ist ebenso der Fall, wenn eine Verschärfung der Regel oder Einschränkung des Scopes entweder nicht möglich ist oder eine Implementierung der Regeln nicht möglich macht. Dennoch können statische Analysesysteme verwendet werden, um manuelle Reviews zu unterstützen.

Beispiel für automatisiertes Review.
Beispiel für automatisiertes Review. (Bild: Axivion)

Hier ist die grundsätzliche Idee, Stellen im Quellcode zu markieren, an denen ein bestimmter Fehler potenziell auftreten könnte, ohne dabei zu prüfen, ob dieser tatsächlich auftritt. Diese Markierungen können dann im Rahmen eines Reviews verwendet werden, um typischen Schwächen manueller Analysen entgegenzuwirken:

  • Bei korrekter Implementation werden alle Stellen gemeldet, die für ein Review relevant sind. Das verringert die Gefahr, dass potenzielle Probleme übersehen werden.
  • Da die Suche nach relevanten Positionen entfällt, wird der zeitliche Aufwand manueller Reviews reduziert.
  • Möglicherweise können einzelne Positionen von der Markierung und damit dem Review ausgeschlossen werden. Während die erreichbare Präzision hier möglicherweise nicht für eine vollständig automatisierte Regel ausreicht, kann Aufwand im Reviewprozess reduziert werden. Hier entfällt – unterstützt durch die statische Analyse – zumindest teilweise die manuelle Klassifikation durch die Reviewer, was ebenfalls die Präzision und Geschwindigkeit der Reviews erhöht.
  • Die für ein Review markierten Stellen werden in den Datenbanken der statischen Analysetools gehalten und können so über die Zeit verfolgt werden. Weiterhin stehen die für Analysewerkzeuge üblichen Verfahren für Suppressions und Justifications zur Verfügung. Diese können etwa genutzt werden, um Reviewergebnisse zu dokumentieren. Der Overhead für die Synchronisation manueller und automatisierter Reviews wird so deutlich reduziert.
  • Reviewmarkierungen und dazugehörige Kommentare können in das reguläre Reporting aufgenommen werden. Es gibt mit den Daten aus den Analysewerkzeugen nur noch eine „Quelle der Wahrheit“. Synchronisationsaufwand entfällt und Nachweise über die Prüfungen insgesamt (automatisch wie manuell) sind einfacher zu erbringen.
  • Insgesamt muss auch bei Verwendung statischer Analysetools zur Reviewunterstützung sichergestellt werden, dass einmal erfasste Reviewergebnisse aktuell bleiben. Ein periodisches Re-Review ist notwendig, um beispielsweise Justifications und Suppressions zu aktualisieren oder zu entfernen. Auch hierbei kann die durch die Tools gespeicherten Daten über den zeitlichen Verlauf hilfreich sein. (neu)

Autor

Autor Sebastian Krings
(Bild: Axivion)

Sebastian Krings ist Softwareengineer bei Axivion

Sie möchten gerne weiterlesen?