Filip Pizlo präsentierte auf der Konferenz SPLASH’24 den Open-Source C/C++ Compiler Fil-C, der Schutz vor speicherbezogenen Fehlern bietet. Das Projekt zielt darauf ab, vollständige Kompatibilität mit bestehendem Code in C und C++ zu gewährleisten — es genügt, den vorhandenen Code neu zu kompilieren, um eine sichere Speicherverwaltung zu erreichen. Der Compiler wurde unter Verwendung von Komponenten des LLVM-Projekts entwickelt und steht unter der Lizenz Apache 2.0. Die Runtime wird unter der BSD-Lizenz bereitgestellt. Im Gegensatz zum kürzlich angekündigten Projekt TrapC, das sich noch in der Entwurfsphase befindet, ist der Compiler Fil-C bereits bereit, bestehenden Code zu kompilieren.
Das Projekt bietet sicher im Speicher arbeitende Alternativen zu den Standard-C-Bibliotheken (libc) und C++ (libc++), basierend auf den Musl- und LLVM libc++-Bibliotheken. Unter anderem können in den kompilierten Programmen Funktionen wie Multithreading, Signalverarbeitung, Datei-Mapping in den Speicher (mmap), longjmp/setjmp und Exception-Handling in C++ verwendet werden. Mit Fil-C können die Projekte bzip2, zip, pcre und ncurses ohne Änderungen kompiliert werden. Mit geringfügigen Anpassungen ist die Compilation von OpenSSH, OpenSSL, CPython, SQLite, Lua, Curl, Lynx, jpeg6b, zsh, xzutils und simdutf möglich.
Der Schutz vor Speicherproblemen wird durch die Verwendung von 128-Bit MonoCaps-Zeigern mit Metadaten zur Typprüfung und zur Überwachung der Puffergrenzen gewährleistet, sowie durch einen FUGC-Garbage-Collector, der alle Speicherzuweisungs- und -freigabebetriebe überwacht. Bei Speicherfehlern wird die Anwendung sofort abgestürzt, was das Ausnutzen möglicher Schwachstellen verhindert.
Es wird berichtet, dass die Kombination aus MonoCaps und FUGC dabei hilft, alle Fehler zu erfassen und zu blockieren, die mit Pufferüberläufen im Stack und Heap, dem Zugriff auf bereits freigegebenen Speicher, Race Conditions bei der Arbeit mit Zeigern sowie falscher Typverarbeitung (Type Confusion) im Kontext von der Überlappung von Zeigertypen und Nicht-Zeigertypen, Problemen beim dynamischen Linken und der fehlerhaften Verwendung von va_lists zusammenhängen. Darüber hinaus überprüft Fil-C separat die Grenzen und Typen in den Buffern, die bei Systemaufrufen übergeben werden.
Die Möglichkeit, Fil-C zum Schutz bestehender Projekte zu verwenden, ohne deren Code neu schreiben oder spezielle Sprachkonstrukte einführen zu müssen, geht jedoch mit einer Leistungseinbuße einher. In der aktuellen Entwicklungsphase sind die mit Fil-C kompilierten Programme etwa 1,5 bis 5 mal langsamer im Vergleich zu herkömmlichen Compilern. Geplant ist die Durchführung von Optimierungen. Nach Abschluss dieser Arbeiten wird erwartet, dass der Code in den meisten Fällen nur um das 1,2-fache langsamer ausgeführt wird, während in den ungünstigsten Szenarien die Verlangsamung 1,5-fach nicht überschreiten wird. Der Compiler unterstützt derzeit nur die Linux-Plattform auf X86_64-Systemen. Ältere Versionen unterstützten macOS und FreeBSD, jedoch wurde beschlossen, die Anstrengungen nicht zu verbreiten und zunächst einen qualitativ hochwertigen libc-Port für eine Plattform vorzubereiten.
Eine weitere Einschränkung von Fil-C ist der grundlegende Verzicht auf die Wahrung der ABI-Kompatibilität für C/C++-Code, was es unmöglich macht, den in Fil-C erzeugten Code mit von anderen Compilern zusammengestellten Bibliotheken und Objektdateien zu verknüpfen. Die Methoden zum Funktionsaufruf und das Verfahren zur dynamischen Verknüpfung in Fil-C unterscheiden sich von bestehenden Compilern und Linkern. Diese Entscheidung erklärt sich dadurch, dass bei der Verknüpfung mit ungeschütztem Code der Sinn des in Fil-C angebotenen Schutzes verloren geht und die Illusion einer geschützten Anwendung entsteht — bei bestehender ABI-Kompatibilität hätten Entwickler versucht, in Fil-C lediglich einzelne Dateien zu kompilieren, ohne sich die Mühe zu machen, das gesamte Projekt zu portieren.
Der in Fil-C verwendete MonoCap-Mechanismus basiert auf der Verwendung von 16-Byte-Zeigern, in denen neben der Speicheradresse auch ein Verweis auf ein Objekt angegeben wird, das Informationen über die Fähigkeiten (capability) enthält, wie z. B. obere und untere Grenzen des mit dem Zeiger assoziierten Puffers sowie ein Array, das die Datentypen definiert, die in jedem Speicherblock gespeichert sind (1 Byte mit Informationen zum Typ (unset, int, ptr, free) für jeden 16-Byte-Speicherblock). Bei jedem Zugriff auf den Speicher über den Zeiger erfolgt eine Überprüfung der Grenzen und des Typs (z. B. können in Speicher mit dem Typ „ptr“ keine Daten mit dem Typ „int“ geschrieben werden und umgekehrt).
Alle Vorgänge zur Allokation und Freigabe von Speicher werden vom FUGC (Fil's Unbelievable Garbage Collector) verarbeitet. Bei der Freigabe von Speicher wird jede mit dem freizugebenden Puffer verbundene Typinformation auf "free" gesetzt, und alle Zeiger auf die freigegebenen Objekte werden auf ein separates Objekt umgeleitet, das signalisiert, dass der Speicher bereits freigegeben wurde. Jeglicher weitere Zugriff auf einen Datensatz mit dem Typ "free" oder über einen Zeiger, der mit einem freigegebenen Objekt verbunden ist, führt zur Generierung einer Ausnahme, was dazu beiträgt, sich vor vulnerabilities der Art use-after-free zu schützen. Der Garbage Collector arbeitet parallel und hält die Ausführung anderer Threads nicht an.
Die Verwendung einer Kombination aus MonoCaps und FUGC ermöglicht es, die gewohnte Arbeit mit Zeigern beizubehalten und die Semantik der Aufrufe von malloc und free unverändert zu lassen, während gleichzeitig garantiert wird, dass Schutz besteht. Der Programmcode kann verschiedene logische Fehler enthalten, wie falsche Typumwandlungen, fehlerhafte arithmetische Operationen mit Zeigern, Race Conditions und ein verspäteter Aufruf der Funktion free(). Unabhängig davon wird Fil-C die ursprünglichen Grenzen und den Datentyp speichern und die Ausführung unterbrechen, wenn versucht wird, über einen Zeiger auf einen Bereich außerhalb der gespeicherten Grenzen zuzugreifen, auf einen freigegebenen Speicherblock zuzugreifen oder Daten des Typs 'int' wie einen Zeiger oder umgekehrt zu lesen.
Fil-C, Filip Pizlo, ist Direktor bei Epic Games und verantwortlich für Projekte im Zusammenhang mit Programmiersprachen. Filip bringt umfangreiche Erfahrungen in der Arbeit mit virtuellen Maschinen, Programmiersprachen, Compilern und Garbage Collectors mit. So entwickelte er bei IBM die Programmiersprache X10, arbeitete bei Microsoft an den Garbage Collectors Stopless, Clover und Chicken, und war bei Apple für den JIT-Compiler sowie die Optimierung der Browser-Engine WebKit zuständig. Bei Epic Games leitet er das Team, das die Programmiersprache Verse und die zugehörige virtuelle Maschine entwickelt. Filip ist auch einer der Hauptentwickler von virtuellen Maschinen Jikes RVM, Ovm und Fiji VM.

Quelle: opennet.ru
