© saicle/Shutterstock.com
Wie Rust durch Eliminierung kritischer Funktionen Sicherheit schafft

Rostiges C


Yoda postulierte vor vielen Jahren, dass große Macht mit großer Verantwortung einhergeht. Im Fall der Programmiersprachenfamilie C und C++ ist dies eindeutig der Fall: Es gibt kaum eine andere Sprachgruppe, in der man sich so einfach eine Kartusche in den Fuß jagen kann. Die von Mozilla entwickelte Sprache Rust – der Name bedeutet auf Englisch soviel wie Rost – möchte Entwicklern eine von kritischen Teilen befreite Alternative zu C anbieten. Dieser Artikel stellt Ihnen besonders interessante Aspekte von Rust in Kurzform vor.

Aus Sicht der Sprachdesigner ist das ersonnene Konzept einfach: Wenn man kritische Funktionen schlichtweg deaktiviert, so kann der Entwickler diese nicht zur Erstellung von unsicherem Code benutzen. C ist unter anderem deshalb so populär, weil es eine direkte Beziehung zwischen geschriebenem Code und resultierendem Maschinenverhalten gibt. Die Einführung von Garbage Collectors und anderen Niedlichkeiten würde an dieser Stelle für Unruhe sorgen – Rust verzichtet auf diese nicht unbedingt nützlichen Errungenschaften der Compilerforschung. Die Unterstellung von Luddismus ist – trotz dieses konservativen Vorgehens – unfair. Rust mag auf den ersten Blick wie C aussehen, ist aber an entscheidenden Stellen trotz allem anders. Neben diversen Kniffen zur Verkürzung des Codes bietet Rust zudem einige Änderungen, die man als Entwickler nicht unbedingt erwartet.

Erste Schritte

Wie es sich für einen Blick über den Tellerrand gehört, wollen wir uns mit einer vergleichsweise einfachen Einrichtung des Entwicklungssystems befassen. Laden Sie das für Ihr Betriebssystem geeignete Archiv unter [1] herunter und entpacken Sie es an einem für Sie angenehmen Ort.

In den folgenden Schritten wird mit Ubuntu 13.04 gearbeitet; das Betriebssystem liegt aus historischen Gründen in der 32-Bit-Variante vor. Da Rust auf einer Gruppe verschiedener Bibliotheken basiert, ist vor dem Einsatz eine Installation per Shellskript notwendig:

tamhan@ubuntu:~/Arbeitsfläche/rust-nightly-i686-unknown-linux-gnu$ sudo ./install.sh [sudo] password for tamhan: install: creating uninstall script at /usr/local/lib/rustlib/uninstall.sh . . . Rust is ready to roll. 

Install.sh baut den 600 MB großen Interpreter tief in das Hostbetriebssystem ein. Wenn Sie Ihre Experimente mit der Sprache beenden möchten, sollten Sie den ausgegebenen Pfad zum Deinstallationswerkzeug notieren. Die Ausführung eines Beispielprogramms erfolgt dann gemäß dem von gcc, g++ und Co bekannten Schema:

tamhan@ubuntu:~/Arbeitsfläche/Rust$ rustc hello.rs tamhan@ubuntu:~/Arbeitsfläche/Rust$ ./hello hello? 

Von rustc erstellte Binärdateien haben normalerweise den Namen der Quelldatei, die die Main-Methode enthält. Achten Sie zudem darauf, dass das Rust-Team seinen Interpreter normalerweise in Form von als Daily bezeichneten und tagesaktuell erstellten Kompilaten ausliefert. In diesem Artikel kam folgende Version zum Einsatz:

tamhan@ubuntu:~/$ rustc -V rustc 1.0.0-nightly (890293655 2015-02-28) (built 2015-03-01) 

Da Rust seit mehr als fünf Jahren am Markt ist, hat sich eine Vielzahl verschiedener IDEs etabliert. Neben dem universell beliebten Sublime gibt es auch Erweiterungen für einige andere populäre Produkte.

Hello Rust

Als erste Amtshandlung wollen wir ein Hello-World-Programm realisieren. Der dazu notwendige Code erinnert verdächtig an C und sieht so aus:

fn main() { println!("hello?"); }

println ist in Rust als Makro deklariert – der Name der Methode endet aus diesem Grund mit einem Ausrufezeichen. Rust-Makros unterscheiden sich insofern von C bzw. C++, als der für ihre Ausführung zuständige Interpreter wesentlich intelligenter ist. Leider sind zu ihrem Verständnis fortgeschrittene Sprachkonstrukte notwendig, die wir in diesem Artikel nicht vorstellen können.

Typisierung: Ärger!

Ein gravierender Unterschied zwischen C und Rust offenbart sich beim Versuch der Variablenerstellung. Die Methode playSomeVars() zeigt einige Besonderheiten des Rust-Interpreters auf:

fn playSomeVars() { let var_a=22; let var_b="Hallo"; let an_int:int=128; }

Variablendeklarationen beginnen in Rust mit dem aus älteren Basic-Interpretern bekannten let-Schlüsselwort. Der Compiler kann den Typ einer Variablen in vielen Fällen anhand des übergebenen Initialisators erkennen – das dedizierte Angeben des Datentyps ist nur bei Konstanten und ähnlichen Sonderfällen notwendig. Dabei stehen die in Tabelle 1 aufgelisteten Konstrukte zur Verfügung.

Schlüsselworte

Bedeutung

bool

Boolesche Variable

i8, i16, i32, i64

Integervariablen

u8, u16, u32, u64

Vorzeichenlose Integervariablen

char

Unicode-Zeichen

f32, f64

Gleitkommazahlen

()

„Unit-Typ“, dessen Wert stets () sein muss. Void-artige Abstraktion, die das versehentliche Zurückgeben von Werten aus Funktionen erschwert

Tabelle 1: Angabe des Datentyps

rustc zwingt Entwickler von Haus aus zur Verwendung des Snake-Case-Namensschemas. playSomeVars müsste play_some_vars heißen – da wir uns hier aus didaktischen Gründen nicht an die Vorgabe halten, belohnt uns der Compiler mit der in Abbildung 1 gezeigten Fehlermeldung.

hanna_rust_1.tif_fmt1.jpgAbb. 1: Großbuchstaben sind böse!

Wem gehört was?

Sicherheitslücken und Race Conditions entstehen oft durch unklare Besitzverhältnisse: Wenn eine Routine nicht weiß, was ihre Kollegin mit einem geteilten Datenobjekt anstellt, so entsteht Heckmeck. Die einfachste Absicherung ist in einer Abart von playSomeVars gezeigt – sie versucht, eine nicht als mutabel gekennzeichnete Variable nach der Erstellung zu beschreiben. Rust reagiert auf Kompilationsversuche mit der Anzeige einer nach dem Schema „re-assignment of immutable vari­able“ aufgebauten Fehlermeldung:

fn playSomeVars() { . . . let an_int:int=128; an_int=25; }

Per let deklarierte Variablen sind unveränderbar, Änderungen am in ihnen gespeicherten Wert werden vom Compiler gnadenlos abgelehnt. Zur Umgehung dieses Problems genügt es, das Feld nach folgendem Schema als veränderbar zu markieren:

fn playSomeVars() { . . . let mut an_int:int=128; an_int=25; }

Für die Vorführung der Eigentümerüberprüfung müssen wir ein weiteres neues Sprachelement einführen. Das folgende Statement weist Rust zur Deklaration eines Structs an, das – ganz an...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang