RISCV-Fuzzer für GCC und LLVM
- Rajeev Gadgil

- vor 4 Tagen
- 3 Min. Lesezeit
Das Fuzzing von RISC-V-Compilern wie GCC und LLVM ist eine unerlässliche Methode, um die Korrektheit und Sicherheit des gesamten auf dieser Architektur basierenden Software-Ökosystems zu gewährleisten. Dabei geht es nicht darum, Schwachstellen im kompilierten Code zu finden, sondern vielmehr darum, Fehler im Compiler selbst aufzudecken, die zu fehlerhafter Codegenerierung, unerwartetem Verhalten oder sogar ausnutzbaren Sicherheitslücken führen könnten.
Warum Compiler-Fuzzing eine einzigartige Herausforderung darstellt
Das Fuzzing von Compilern unterscheidet sich vom Fuzzing typischer Anwendungen. Anstatt einem Programm zufällige Daten zuzuführen, generiert man zufälligen, aber syntaktisch korrekten Quellcode, der dem Compiler übergeben wird. Ein einfacher Fuzzer, der lediglich Bytes verändert, erzeugt schnell Code, der nicht einmal geparst werden kann, und übersieht so tieferliegende Fehler.
Das Hauptziel des Compiler-Fuzzings ist die Erkennung zweier Haupttypen von Fehlern:

Abstürze und Panikzustände: Der Fuzzer generiert Code, der während der Kompilierung zum Absturz, zum Hängenbleiben oder zu einem schwerwiegenden Fehler des Compilers führt. Dies deutet auf einen Compilerfehler hin, der behoben werden muss.
Fehlkompilierungen: Dies ist die gefährlichste Art von Fehlern. Der Compiler kompiliert den getesteten Code zwar erfolgreich, der generierte Maschinencode (RISC-V-Assembler) ist jedoch fehlerhaft. Dies kann zu unbemerkten Datenbeschädigungen, Sicherheitslücken oder unvorhersehbarem Programmverhalten führen. Um solche Fehler zu finden, ist eine Technik namens differentielles Fuzzing erforderlich .
Die Leistungsfähigkeit des differentiellen Fuzzing für RISC-V-Compiler
Differential Fuzzing ist eine außerordentlich leistungsstarke Technik zum Auffinden von Fehlkompilierungen in RISC-V-Compilern. So funktioniert es:

Ein Fuzzer, oft ein Programm, das gültigen C- oder C++-Code generiert (wie z. B. csmith), erzeugt ein einzigartiges Programm.
Dieses Programm wird von mindestens zwei verschiedenen Compilern (z. B. GCC und LLVM) oder mit unterschiedlichen Optimierungsflags (z. B. -O0 und -O3) kompiliert.
Die kompilierten Binärdateien werden anschließend ausgeführt und ihre Ausgaben verglichen.
Stimmen die Ausgaben nicht überein, bedeutet dies, dass mindestens einer der Compiler einen Kompilierungsfehler aufweist. Der Fuzzer speichert diesen spezifischen Quellcode dann als Testfall zur Analyse durch einen Entwickler.
Diese Methode nutzt im Prinzip ein „Testorakel“, um Fehler automatisch zu identifizieren, ohne dass die korrekte Ausgabe vorher bekannt sein muss. Dies ist ein Hauptgrund dafür, dass so viele Compilerfehler sowohl in GCC als auch in LLVM gefunden wurden.
Wichtige Werkzeuge und Repositories für das Fuzzing von RISC-V-Compilern
Während viele der zuvor erwähnten Allzweck-Fuzzer (wie AFL++) zum Fuzzing des Quellcodes eines Compilers verwendet werden können, sind oft spezialisierte Werkzeuge erforderlich, um gültigen RISC-V-spezifischen Code effektiv zu generieren und zu testen.
csmith: Dies ist ein bekannter, randomisierter Testfallgenerator für C-Programme. Er erzeugt komplexen, gültigen C-Code, der sich ideal für differentielle Tests von C-Compilern wie GCC und LLVM eignet. Obwohl er nicht RISC-V-spezifisch ist, ist er ein unverzichtbarer Bestandteil des Workflows zum Fuzzing des RISC-V-Backends jedes C-Compilers.
GitHub-Repository: https://github.com/csmith-project/csmith
RISCV-DV: Dieses von der RISC-V-Community gepflegte Tool dient primär der Designverifikation von RISC-V-Prozessoren, kann aber auch zur Generierung komplexer Befehlssequenzen für das Testen von Compiler-Backends verwendet werden. Es ist hochgradig konfigurierbar und kann spezifische ISA-Erweiterungen ansprechen.
GitHub-Repository: https://github.com/google/riscv-dv
IRFuzzer: Ein spezialisierter Fuzzer für das LLVM-Backend . Anstatt C/C++-Quellcode zu generieren, erzeugt er die LLVM-Zwischenrepräsentation (IR). Dadurch kann er die Backend-Codegenerierung direkt testen, ohne sich um Frontend-Fehler kümmern zu müssen. Dies ist ein sehr gezielter Ansatz, um Probleme im RISC-V-Codegenerator von LLVM zu finden.
GitHub: Als Recherchetool findet man häufig Ressourcen auf arXiv und Universitätswebseiten. Die Suche nach „IRFuzzer“ auf GitHub führt zu verwandten Projekten.
RISCV-Vector-Intrinsic-Fuzzing (RIF): Ein spezieller Fuzzer, der mithilfe der RISC-V Vector Extension (RVV)-Intrinsics Zufallscode generiert. Dies ist unerlässlich, um zu überprüfen, ob Compiler wie GCC und LLVM diesen komplexen und leistungskritischen Teil der RISC-V-ISA korrekt implementieren.
GitHub-Repository: https://github.com/sifive/riscv-vector-intrinsic-fuzzing
Patrick-rivos/compiler-fuzz-ci: Dieses GitHub-Repository bietet ein hervorragendes Beispiel für eine Continuous-Integration-Umgebung (CI) zum Fuzzing von RISC-V-Compilern. Es zeigt, wie man Tools wie csmith mit einer CI-Pipeline kombiniert, um GCC und LLVM automatisch zu fuzzing und Fehler zu melden.
GitHub-Repository: https://github.com/patrick-rivos/compiler-fuzz-ci
Das Fuzzing von RISC-V-Compilern ist eine fortlaufende und entscheidende Aufgabe. Es stellt sicher, dass die Softwareentwickler darauf aufbauend zuverlässig, sicher und korrekt auf die zugrunde liegende Hardware übersetzt wird, wodurch das gesamte RISC-V-Ökosystem gestärkt wird.





Kommentare