Betrachten wir zwei Programme, die beide alle Ihre Tests bestehen. Eines ist korrekt. Das andere enthält einen Division-durch-Null-Fehler, der nur bei einer bestimmten Kombination von Eingaben gleichzeitig auftritt – einer Kombination, die Ihre Tests nie erzeugen. Traditionelle Testverfahren können nicht feststellen, welches Programm welches ist. Abstrakte Interpretation hingegen schon.
Die abstrakte Interpretation ist der mathematische Rahmen, der es Werkzeugen zur statischen Programmanalyse ermöglicht, über alle möglichen Verhaltensweisen eines Programms zu argumentieren, ohne es auszuführen. Sie ist die Technik, die Facebooks Infer dabei unterstützt, Nullzeigerfehler in großem Umfang aufzuspüren, die dem Astrée-Analysator zur formalen Verifizierung der Airbus-Flugsteuerungssoftware zugrunde liegt und die jedem statischen Analysator zugrunde liegt, der Korrektheit beansprucht – die Garantie, dass ein Programm, das die Analyse besteht, tatsächlich frei von den geprüften Fehlern ist. Das Verständnis ihrer Funktionsweise erklärt, warum manche Werkzeuge Fehler finden, die anderen entgehen, und warum diese Garantien mit bestimmten Kompromissen einhergehen.
Code analysieren, ohne ihn auszuführen
SMART TS XL wendet gleichzeitig eine strukturelle statische Analyse auf alle Sprachen Ihres Portfolios an.
Mehr InfosWas ist abstrakte Interpretation?
Die abstrakte Interpretation ist eine Theorie der Programmapproximation, die 1977 von Patrick Cousot und Radhia Cousot entwickelt wurde. Die Kernidee: Anstatt die exakte Menge aller möglichen Programmzustände zu berechnen, was im Allgemeinen unentscheidbar ist, berechnet man eine sichere Überapproximation mithilfe eines vereinfachten mathematischen Bereichs, der sich gut analysieren lässt.
Das Wort „abstrakt“ bedeutet hier nicht vage oder konzeptuell. Es bezieht sich auf eine spezifische mathematische Operation: die Abstraktion einer Menge konkreter Werte in eine einfachere Darstellung, die die relevanten Eigenschaften beibehält und gleichzeitig unnötige Details ausblendet. Ein konkreter ganzzahliger Wert wie 42 In einer Abstraktion der Vorzeichenanalyse wird daraus einfach „positiv“. Die Abstraktion verliert zwar Information (man kennt den genauen Wert nicht mehr), gewinnt aber an Handhabbarkeit (das Vorzeichen einer ganzen Zahl ist eine von drei Möglichkeiten: positiv, negativ oder null).
Der Nutzen dieser Methode für die Programmanalyse liegt in der damit verbundenen Garantie: Findet die Analyse keinen Fehler im abstrahierten Bereich, so existiert auch in der konkreten Ausführung kein Fehler. Findet sie einen potenziellen Fehler, so kann dieser in der Praxis auftreten oder auch nicht; ein tatsächlicher Fehler kann jedoch nicht verborgen bleiben. Das ist Korrektheit.
Abstrakte Interpretation vs. AST-Analyse vs. Dynamische Analyse
Diese Begriffe werden häufig verwechselt; in den Suchergebnissen für diesen Artikel erscheint beispielsweise „AST-Codeanalyse“, und sie beschreiben unterschiedliche Dinge.
Ein abstrakter Syntaxbaum (AST) ist eine Datenstruktur, die die grammatikalische Struktur von Quellcode repräsentiert. Jeder Compiler und Linter erstellt einen AST. Er bildet die Grundlage für Parsing, Refactoring-Tools und musterbasierte statische Analyse. Die AST-basierte Analyse findet Muster: Code, der einer Regel entspricht (z. B. eine Funktion mit zu vielen Parametern oder eine durch Verkettung erstellte SQL-Zeichenkette), wird markiert. Sie analysiert weder Werte noch das Laufzeitverhalten.
Abstrakte Interpretation analysiert das Laufzeitverhalten, ohne das Programm auszuführen. Sie verwendet den abstrakten Syntaxbaum (AST) als Eingabe, geht aber weit darüber hinaus: Sie modelliert den Wertefluss im Programm, die Wertebereiche von Variablen, ob ein Zeiger an einer bestimmten Aufrufstelle null sein kann und ob eine Schleife terminiert. Die AST-Analyse ist Mustererkennung. Abstrakte Interpretation ist Verhaltensanalyse.
Die meisten Linter (ESLint, Checkstyle, Pylint) basieren primär auf dem abstrakten Syntaxbaum (AST). Die meisten formalen Verifikationswerkzeuge (Infer, Astrée, Polyspace) nutzen abstrakte Interpretation. Dynamische Analyse (Ausführen des Programms und Beobachten des tatsächlichen Verhaltens) findet nur Fehler, die durch bestimmte Eingaben ausgelöst werden. Abstrakte Interpretation hingegen findet Fehler für alle möglichen Eingaben, ohne das Programm überhaupt auszuführen.
Die mathematischen Prinzipien der statischen Analyse
Die Anfrage „Welche mathematischen Prinzipien liegen statischen Analysewerkzeugen zugrunde?“ erscheint direkt in den Suchergebnissen. Hier ist die einfache Antwort.
Die abstrakte Interpretation beruht auf drei mathematischen Strukturen:
Verbände. Ein Verband ist eine partiell geordnete Menge, in der jedes Paar von Elementen eine kleinste obere Schranke (Verbindung) und eine größte untere Schranke (Schnitt) besitzt. In der statischen Analysis repräsentiert der Verband den abstrakten Definitionsbereich, die Menge aller möglichen abstrakten Werte, geordnet nach ihrem Informationsgehalt. Für die Vorzeichenanalyse sieht der Verband folgendermaßen aus:
⊤ (unknown -- could be anything)
/ \
pos neg
\ /
0
|
⊥ (unreachable -- no possible value)
Das Aufsteigen im Gitter bedeutet Genauigkeitsverlust (weniger Wissen). Das Absteigen bedeutet Genauigkeitsgewinn (mehr Wissen). Das oberste Element ⊤ bedeutet „Wir wissen nichts Brauchbares“. Das unterste Element ⊥ bedeutet „Dieser Zustand ist unerreichbar“.
Galois-Verbindungen. Eine Galois-Verbindung ist die formale Beziehung zwischen dem konkreten Bereich (tatsächliche Programmwerte) und dem abstrakten Bereich (der vereinfachten Repräsentation). Sie besteht aus zwei Funktionen: einer Abstraktionsfunktion α, die konkrete Werte auf ihre abstrakte Repräsentation abbildet, und einer Konkretisierungsfunktion γ, die abstrakte Werte wieder auf die Menge der von ihnen repräsentierten konkreten Werte abbildet.
Die entscheidende Eigenschaft: Der abstrakte Bereich muss eine sichere Überapproximation sein. γ(α(S)) ⊇ S Für jede konkrete Menge S gilt: Die Abstraktion kann mehr Werte umfassen, als tatsächlich vorkommen – dadurch entstehen falsch-positive Ergebnisse –, sie darf aber niemals tatsächlich vorkommende Werte ausschließen. Das Ausschließen realer Werte würde bedeuten, dass echte Fehler übersehen werden.
Fixpunktiteration. Bei Programmen mit Schleifen muss die Analyse so lange wiederholt werden, bis ein stabiler Zustand erreicht ist. Beispiel:
c
int x = 0;
while (condition) {
x = x + 1;
}
Bei der ersten Iteration x is {0}Nach einem Schleifenkörper, x könnte sein {0, 1}Nach zwei, {0, 1, 2}Diese Menge wächst ständig, sie stabilisiert sich nie von selbst. Die Lösung ist Erweiterung: ein Operator, der die Konvergenz erzwingt, indem er zu einer breiteren Näherung springt (typischerweise [0, +∞) (für die Intervallanalyse). Die Analyse verwendet dann Verengung um eine gewisse Präzision wiederherzustellen.
Diese Fixpunktberechnung ist es, die die abstrakte Interpretation über alle Ausführungspfade hinweg, einschließlich Schleifen, vollständig macht und sie rechenaufwändiger als einfaches Mustervergleichs-Verfahren macht.
Abstrakte Domänen: Auswahl dessen, was approximiert werden soll
Der abstrakte Bereich bestimmt, was die Analyse finden kann und was nicht. Unterschiedliche Bereiche beantworten unterschiedliche Fragen zum Programmverhalten.
| Abstrakter Bereich | Was es verfolgt | Anwendungsbeispiel | Was es fehlt |
|---|---|---|---|
| Zeichenanalyse | Ob die Werte positiv, negativ oder null sind | Division durch Nullerkennung | Exakte Werte, Überlaufbedingungen |
| Intervallanalyse | Ober- und Untergrenzen für numerische Werte | Pufferüberlauf, Array-Zugriffssicherheit | Beziehungen zwischen Variablen |
| Achteckdomäne | Lineare Beziehungen zwischen Variablenpaaren | Präzisere Überlauferkennung | Nichtlineare Beziehungen |
| Zeigeranalyse | Ob Zeiger null sein oder sich gegenseitig als Aliase bezeichnen dürfen | Null-Dereferenzierung, Verwendung nach dem Freigeben | Objektlebensdauer, Heap-Form |
| Fleckenanalyse | Ob Werte aus nicht vertrauenswürdigen Quellen stammen | SQL-Injection, XSS-Erkennung | Implizite Flüsse durch Kontrolle |
| Polyedrische Domäne | Beliebige lineare arithmetische Beschränkungen | Schleifengebundene Überprüfung | Die Leistungskosten skalieren exponentiell. |
Der Kompromiss zwischen verschiedenen Domänen besteht stets im Spannungsverhältnis zwischen Präzision und Leistung. Die Intervalldomäne ist schnell und deckt die meisten numerischen Fehler auf. Die Polyederdomäne ist deutlich präziser, ihre Komplexität steigt jedoch exponentiell mit der Anzahl der Variablen. Praktische statische Analysewerkzeuge wählen Domänen, die den optimalen Kompromiss für ihre Zielanwendung finden: Sicherheitskritische eingebettete Systeme können eine langsamere, aber präzisere Analyse verkraften; CI/CD-integrierte Linter müssen innerhalb von Sekunden fertig sein.
Wie drei reale Werkzeuge abstrakte Interpretation nutzen
Anstatt die Theorie isoliert zu beschreiben, machen konkrete Werkzeuge ihre Anwendung deutlich.
Facebook Infer verwendet eine Form der abstrakten Interpretation namens Bi-Abduktion, um Java, C, C++ und Objective-C auf Nullzeiger-Dereferenzierungen, Ressourcenlecks und Race Conditions zu analysieren. Bi-Abduktion erkennt automatisch Vor- und Nachbedingungen von Funktionen und ermöglicht so die prozedurübergreifende Analyse ohne manuelle Spezifikationen. Infer wird in CI-Umgebungen von Facebook, Spotify, Mozilla und Dutzenden weiterer großer Unternehmen eingesetzt, da es sich für Codebasen mit mehreren Millionen Zeilen skalieren lässt und dabei für die geprüften Fehlerklassen zuverlässig bleibt.
Astrée verwendet abstrakte Interpretation mit numerischen abstrakten Domänen, um die Fehlerfreiheit von C-Programmen zur Laufzeit nachzuweisen. Airbus nutzte das Verfahren zur formalen Verifizierung der primären Flugsteuerungssoftware des A380 und bewies damit die Fehlerfreiheit des gesamten Steuerungssystems – eine Garantie, die kein Testprogramm bieten konnte. Astrée findet keine falsch-negativen Ergebnisse für die geprüften Fehlerklassen, kann aber falsch-positive Ergebnisse liefern, die eine manuelle Überprüfung erfordern.
Polyspace (MathWorks) verwendet abstrakte Interpretation für eingebetteten C- und C++-Code in sicherheitskritischen Anwendungen. Jede Operation wird als „grün“ (nachweislich kein Fehler), „rot“ (definitiv ein Fehler) oder „orange“ (potenzieller Fehler, der eine Überprüfung erfordert) klassifiziert. Die grüne Klassifizierung ist ein formaler Beweis: Bei dieser Operation kann keine Ausführung einen Laufzeitfehler verursachen.
Das Dreieck aus Klangqualität, Präzision und Leistung
Abstrakte Interpretationswerkzeuge bewegen sich in einem grundlegenden Dreieck konkurrierender Eigenschaften. Kein Werkzeug kann alle drei gleichzeitig maximieren.
Zuverlässigkeit bedeutet, dass keine falsch-negativen Ergebnisse vorliegen: Jeder tatsächliche Fehler in der analysierten Klasse wird erkannt. Zuverlässige Werkzeuge bieten Garantien; nicht zuverlässige Werkzeuge können Fehler übersehen.
Präzision bedeutet wenige falsch-positive Ergebnisse: Die Befunde beziehen sich auf reale Probleme und nicht auf theoretische, die nicht auftreten können. Hohe Präzision erfordert differenziertere abstrakte Bereiche und eine interprozedurale Analyse.
Performance bedeutet, dass die Analyse in sinnvoller Zeit abgeschlossen wird. Präzisere Analysen sind aufwändiger. Der Nachweis, dass in einer Codebasis mit einer Million Zeilen keine Laufzeitfehler vorliegen, dauert Stunden; ein Linter-Scan hingegen nur Sekunden.
Unterschiedliche Anwendungen erfordern unterschiedliche Punkte in diesem Dreieck:
- IDE-Flusen und CI/CDLeistung zuerst, Präzision zweitens, Zuverlässigkeit optional
- Sicherheitsscan: Präzision hat oberste Priorität (um die Entwicklermüdigkeit durch Warnmeldungen zu reduzieren), Zuverlässigkeit ist für Klassen mit hohem Schweregrad wichtig
- Sicherheitskritische ZertifizierungKorrektheit hat oberste Priorität (echte Fehler dürfen nicht übersehen werden), Leistung ist zweitrangig; Fehlalarme sind bei manueller Überprüfung akzeptabel.
Abstrakte Interpretation in eingebetteten und sicherheitskritischen Entwicklungssystemen
Die Anfrage „Vorteile der statischen Analyse in der Entwicklung eingebetteter Systeme“ verweist auf eines der wichtigsten Anwendungsgebiete der abstrakten Interpretation. Eingebettete Systeme, Steuergeräte in der Automobilindustrie, Firmware für medizinische Geräte und Flugsteuerungssoftware für die Luft- und Raumfahrt weisen Einschränkungen auf, die die abstrakte Interpretation besonders wertvoll machen:
Es gibt keinen Testaufbau für alle Zustände. Ein Kfz-Steuergerät reagiert in Echtzeit auf Tausende von Sensorkombinationen. Tests für jede einzelne Kombination zu erstellen, ist unmöglich. Eine abstrakte Interpretation deckt alle Zustände gleichzeitig ab.
Zertifizierungsanforderungen. DO-178C (Luft- und Raumfahrt), ISO 26262 (Automobilindustrie) und IEC 62443 (Industriesteuerung) fordern den Nachweis, dass sich Software unter allen Bedingungen korrekt verhält. Formale Verifikation mittels abstrakter Interpretation kann diese Anforderung erfüllen, was mit Testabdeckungsberichten nicht möglich ist.
Ressourcenbeschränkungen. Eingebettete Software verfügt oft über keine Speicherverwaltung, keine Ausnahmebehandlung und keine Betriebssystem-Fallback-Funktion. Ein Laufzeitfehler, eine Nullzeiger-Dereferenzierung oder ein Array-Zugriff außerhalb der Grenzen führen zu einem schwerwiegenden Systemausfall. Die Folgen des Übersehens solcher Fehler sind nicht nur ein Absturzbericht und ein Hotfix, sondern ein Sicherheitsvorfall.
Die Analysatoren Astrée und Polyspace wurden speziell für diesen Anwendungsbereich entwickelt. Ihr Design akzeptiert hohe Falsch-Positiv-Raten und eine langsame Analyse, um im Gegenzug sicherzustellen, dass keine falsch-negativen Ergebnisse durchrutschen.
Falsch-positive Ergebnisse und das sich ausweitende Problem
Der häufigste Kritikpunkt an Werkzeugen zur abstrakten Interpretation sind Fehlalarme, also Warnungen vor potenziellen Fehlern, die in der Praxis gar nicht auftreten können. Versteht man, warum Fehlalarme systembedingt und kein Qualitätsmangel sind, lassen sie sich leichter handhaben.
Falsch-positive Ergebnisse entstehen aus zwei Quellen:
Überapproximation im abstrakten Bereich. Wenn der Intervallbereich verfolgt x ∈ [0, 100]Es kann nicht zwischen Fällen unterscheiden, in denen x ist in der Praxis immer kleiner als 50. Eine Division durch x kann als potenzieller Fehler aufgrund einer Division durch Null gekennzeichnet werden, selbst wenn die Programmlogik dies garantiert. x > 0Eine präzisere Domäne (die den genauen Wert oder eine Verknüpfungsbedingung erfasst). x Eine Änderung der Variablenzuordnung würde zwar das falsch positive Ergebnis eliminieren, wäre aber mit einem höheren Rechenaufwand verbunden.
Erweiterung. Der Konvergenzoperator, der die Schleifenanalyse handhabbar macht, führt zwangsläufig zu Informationsverlust. Nach der Erweiterung x ab [0, 5] zu [0, +∞)Der Analysator weiß nicht mehr, dass x bleibt begrenzt. Wenn der Code prüft assert(x < 1000) Nach der Schleife lässt sich diese Behauptung nicht mehr beweisen, selbst wenn sie in der Praxis x bleibt immer deutlich unter 1000.
Praktische Strategien für den Umgang mit falsch positiven Ergebnissen: Konfigurieren Sie die Analyse so, dass für kritische Module präzisere Domänen verwendet werden (wobei eine langsamere Analyse in Kauf genommen wird), unterdrücken Sie bestätigte falsch positive Ergebnisse mit gezielten Annotationen und behandeln Sie die orangefarbenen/unbekannten Ergebnisse des Tools als priorisierte Überprüfungswarteschlange und nicht als bestätigte Fehler.
Wie SMART TS XL Wendet statische Analysen im Unternehmensmaßstab an
SMART TS XL agiert im Spannungsfeld zwischen abstrakter Interpretationstheorie und Unternehmensrealität: Codebasen, die sich über mehrere Sprachen erstrecken, jahrzehntelange Entwicklung und organisatorische Grenzen, die eine formale Verifikation pro Programm unpraktisch machen.
Anstatt eine einzige abstrakte Domäne auf alle Programme anzuwenden, SMART TS XL statische Code-Analyse kombiniert strukturelle Analysetechniken, die für jede Sprache in der Umgebung geeignet sind, COBOL, JCL, Java, Python, RPG, PL/I, SQL und moderne Stacks, und erzeugt gleichzeitig Qualitätsmetriken, Abhängigkeitsdaten und Sicherheitsergebnisse für das gesamte Portfolio.
Die Funktion zur Abbildung von Anwendungsabhängigkeiten nutzt graphentheoretische Analysen des sprachübergreifenden Aufrufgraphen und identifiziert so die Verbindungen zwischen Programmen, Datensätzen und Jobströmen über Sprachgrenzen hinweg – eine systemweite Analyse, die mit Tools für einzelne Sprachen nicht möglich ist. Es handelt sich hierbei um strukturelles Denken auf Systemebene: Es werden nicht Eigenschaften einzelner Programme, sondern deren Verbindungen nachgewiesen.
Die Auswirkungsanalyse wendet eine Erreichbarkeitsanalyse auf den Abhängigkeitsgraphen an: Ausgehend von einer vorgeschlagenen Änderung an einem Knoten berechnet sie die Menge aller von diesem Knoten aus erreichbaren Knoten. Dies beantwortet die statische Analysefrage „Was wird betroffen sein?“ anhand der Codestruktur und nicht durch Laufzeitbeobachtung oder menschliche Einschätzung.
Für Teams, die durchführen Modernisierung des Altbestands Programme, SMART TS XLDie Strukturanalyse schließt die Lücke zwischen formalen abstrakten Interpretationswerkzeugen (die sprachspezifisch sind und Domänenexpertise für die Konfiguration erfordern) und dem praktischen Bedürfnis, zu verstehen, was große, undokumentierte, mehrsprachige Altsysteme tatsächlich tun – die Voraussetzung für jedes Modernisierungsprogramm, das seine teuersten Überraschungen nicht erst mitten in der Ausführung entdecken möchte.
Häufig gestellte Fragen
Worin besteht der Unterschied zwischen abstrakter Interpretation und Modellprüfung? Beide sind formale Methoden zur Programmverifikation. Abstrakte Interpretation approximiert die Menge möglicher Zustände (korrekt, aber potenziell ungenau). Modellprüfung untersucht den Zustandsraum vollständig (vollständig, aber nur für endliche, beschränkte Systeme durchführbar). Abstrakte Interpretation eignet sich für große Programme. Modellprüfung eignet sich für komplexe Eigenschaften kleinerer Modelle. Sie ergänzen sich, anstatt miteinander zu konkurrieren.
Ist abstrakte Interpretation nur für sicherheitskritische Software geeignet? Nein, obwohl sie dort ihren größten Nutzen entfaltet. Infer wird in Standard-CI/CD-Pipelines großer Technologieunternehmen eingesetzt und findet Nullzeiger-Dereferenzierungen und Ressourcenlecks in alltäglichem Java- und C-Code. Der Grad der angewandten Strenge ist wählbar: von vollständiger Korrektheit mit formalen Garantien bis hin zu einer einfachen heuristischen Analyse, wobei die meisten praktischen Werkzeuge irgendwo dazwischen liegen.
Kann abstrakte Interpretation COBOL analysieren? Abstrakte Interpretation ist als Theorie sprachunabhängig. Ihre Anwendung auf COBOL erfordert die Implementierung der abstrakten Transferfunktionen für COBOL-Operationen, PIC-Feldarithmetik, REDEFINES-Klauseln, Bedingungsnamen der Ebene 88 usw. Allgemeine Werkzeuge für abstrakte Interpretation (Infer, Astrée) unterstützen COBOL nicht. Unternehmensweite Strukturanalyseplattformen, die COBOL nativ verstehen, wenden verwandte statische Analysetechniken an, um Qualitätsprobleme, toten Code und Architekturprobleme in COBOL-Codebasen zu finden.


