Safe and Efficient Execution of LLVM-based Languages

Manuel Rigger


Abstract

In unsafe languages like C/C++, errors such as buffer overflows cause Undefined Behavior. Typically, compilers handle undefined behavior in some arbitrary way, for example, they disregard it when optimizing and omit inserting checks that could detect it. Consequently, undefined behavior often results in hard-to-find bugs and security vulnerabilities, for example, when a buffer overflow corrupts memory.

Existing bug-finding tools that instrument code to detect undefined behavior often suffer from compilers that possibly optimize code so that errors are no longer detected. Alternatively, unsafe code could be rewritten in a safe language like Java, which is well defined and where such errors are detected. However, this would incur an infeasible-high cost for many projects.

To tackle undefined behavior, we came up with an approach to execute unsafe languages on the Java Virtual Machine. We implemented this approach as Safe Sulong, a system that includes an interpreter for unsafe languages, which is written in Java. By relying on Java’s well-definedness and its automatic run-time checks, the interpreter can detect buffer overflows and other errors during its execution and can terminate the program in such cases. Safe Sulong tracks metadata such as types and object bounds, which we provide to programmers over an introspection interface, so that they can use this data to mitigate errors and to implement additional checks. The interpreter also supports unstandardized elements in C code such as the most common inline assembly and GCC builtins. To implement them, we first studied their usage in a large number of open-source projects.

Sulong is used in GraalVM, a commercially-used multi-lingual virtual machine. Since Sulong allows the implementation of efficient native function interfaces, our safe execution mechanism could also make the execution of native extensions of other languages such as Ruby, Python, and R safer.

Kurzfassung

In unsicheren Sprachen wie C/C++ führen Fehler wie Pufferüberläufe zu undefiniertem Verhalten, das von Compilern in der Regel auf undefinierte Weise behandelt wird. Sie ignorieren es zum Beispiel bei Optimierungen und führen keine Überprüfungen durch, die es erkennen könnten. Das führt oft zu schwer lokalisierbaren Fehlern und Sicherheitsl+cken, beispielsweise wenn ein Puffer+berlauf Daten im Speicher zerstört.

Vorhandene Fehlererkennungswerkzeuge, die das Programm zum Erkennen von undefiniertem Verhalten instrumentieren, benutzen oft Compiler, die den Code so optimieren, dass Fehler nicht mehr erkannt werden. Alternativ könnten unsichere Programme in einer sicheren Sprache wie Java neu geschrieben werden, die vollständig definiert und sicher ist. Dies würde jedoch für viele Projekte zu untragbar hohen Kosten führen.

Um undefiniertes Verhalten in den Griff zu bekommen, haben wir einen Ansatz entwickelt, bei dem unsichere Sprachen auf der Java Virtual Machine ausgeführt werden. Der Ansatz wurde unter dem Namen Safe Sulong implementiert, einem System, das auf einem in Java geschriebenen Interpreter für unsichere Sprachen basiert. Indem der Interpreter auf die Wohldefiniertheit und die automatischen Laufzeitprüfungen von Java vertraut, kann er Pufferüberläufe und andere Fehler zur Laufzeit erkennen und das Programm in solchen Fällen abbrechen. Safe Sulong merkt sich Metadaten wie Typen und Objektgrenzen, die Programmierern ¨uber eine Introspektionsschnittstelle zur Verfügung gestellt werden, mit denen sie Fehler abfangen und zusätzliche Prüfungen implementieren können. Der Interpreter unterstützt auch unstandardisierte Elemente in C-Code wie gängige Inline-Assembly-Codestücke und GCC-Builtins. Zu diesem Zweck haben wir zunächst die Verwendung solcher Elemente in einer großen Anzahl von Open-Source-Projekten untersucht.


PhD thesis, Johannes Kepler University Linz, November 2018

Download als pdf