Hochglanzsprache trifft Realität
Rust in Automotive: Sicherer Code, riskanter Mix
Die noch relativ neue Programmiersprache Rust verspricht Sicherheit, doch unter der Haube lauern Risiken. Besonders in der Automobilindustrie kann der kleinste Fehler tödlich enden – und trotzdem bleibt C/C++ im Spiel. Ein riskanter Mix mit Folgen.
Rust ähnelt in ihrer Syntax C und C++, bietet ähnlich viel Flexibilität wie diese älteren Sprachen, jedoch mit deutlich stärkeren Sicherheitsgarantien.
mirsad - stock.adobe.com
Die noch relativ neue Programmiersprache Rust ähnelt in ihrer Syntax C und C++, bietet ähnlich viel Flexibilität wie diese älteren Sprachen, jedoch mit deutlich stärkeren Sicherheitsgarantien, die viele speicherbezogene Probleme überwinden, mit denen C- und C++-Programmierer sowie -Nutzer zu kämpfen haben. Rusts ausdrucksstarke Syntax, starkes Typsystem und hilfreiche Compiler-Fehlermeldungen reduzieren die Wahrscheinlichkeit, dass Fehler entstehen, erheblich. Das ist entscheidend für eingebettete Automobilsysteme, die unter strengen Ressourcenbeschränkungen und Echtzeitanforderungen arbeiten.
Rust entwickelt sich daher zu einer alternativen Wahl für die Entwicklung neuer Softwaremodule in hochkritischen Systemen, wie sie in der Automobilindustrie vorkommen. Aus diesem Grund arbeiten viele große Automobilhersteller mit Rust an Prototypen.
Rust-Nutzer vermeiden viele Speicherprobleme durch strenge Regeln und integrierte Unterstützung bei der Speicherverwaltung. Beispielsweise stellen Prüfungen zur Compile-Zeit sicher, dass Referenzen korrekt verwendet werden. Rust verfügt außerdem über Datentypen mit pointerähnlichen Eigenschaften, die ebenfalls durch diese Compile-Zeit-Prüfungen abgesichert sind. Dadurch werden Probleme verhindert, wie sie typischerweise mit C-ähnlichen Pointern auftreten.
Indem Rust speichersichere Strukturen und Manipulationstechniken bereitstellt, kann die Entwicklung und das Testen von Automobilsoftware beschleunigt werden. Zudem greift es auf moderne Fähigkeiten zurück, die Entwickler heute erlernen und in denen sie geschult werden. Diese beiden Faktoren sind für die Automobilindustrie wichtig, da Komplexität und Umfang der in Fahrzeuge integrierten Softwaresysteme rasant wachsen und kaum nachlassen werden.
Rust ist nicht komplett fehlerresistent
Allerdings bedeutet die Tatsache, dass Rust es schwieriger macht, versehentlich Sicherheits- und Zuverlässigkeitslücken – einschließlich Speicherfehlern – einzuführen, nicht, dass Rust-Programme völlig frei davon sind. Solche Fehler können auf mehreren Wegen trotzdem in Rust-Software gelangen.
Erstens kann ein Rust-Programm Laufzeitfehler erben, wenn es Code aufruft, der in C oder C++ entwickelt wurde. Viele OEMs und Tier-1-Zulieferer, die Rust einsetzen, tun dies, indem sie Rust schrittweise neben Legacy-Code integrieren, um Risiken zu minimieren und gleichzeitig von Rusts Vorteilen zu profitieren. Diese Unternehmen haben jahrzehntelang massiv in C- und C++-Code sowie Toolchains investiert und wollen ihren bestehenden Code verständlicherweise wiederverwenden. Sie scheuen zudem davor zurück, vollständig auf Rust umzusteigen – besonders in sicherheitskritischen Bereichen. Deshalb muss sämtlicher Softwarecode, alt wie neu, überprüft werden, sobald er in ein System integriert wird, das aus großen Anteilen von C, C++, Rust oder weiteren Sprachen besteht.
Zweitens gibt es Anforderungen, die sich nur erfüllen lassen, wenn bestimmte Sicherheitsmechanismen von Rust umgangen werden. Das gilt insbesondere für eingebettete Steuerungssysteme. Viele Low-Level-Operationen, etwa der Zugriff auf speicherabgebildete Hardware-Register oder Datenpuffer, lassen sich unter Rusts strengen Compiler-Prüfungen nicht ohne Weiteres durchführen.
Code, der in Rust einen rohen Pointer dereferenziert, führt beispielsweise zu einem Compile-Fehler, es sei denn, er ist in unsafe {}-Blöcke eingeschlossen. Diese Markierung weist den Compiler an, die üblichen Sicherheitsprüfungen für diesen Code auszusetzen. Dadurch steigt das Risiko, dass Speicherprobleme bis in den Produktionscode gelangen.
Weitere Situationen, in denen Entwickler unsafe {}-Blöcke nutzen müssen, betreffen etwa Unions. Obwohl Unions kein Kernelement von Rust sind, ist ihre Unterstützung wichtig für die Kompatibilität mit C und C++. Da der Compiler nicht garantieren kann, ob ein Schreibzugriff auf ein Feld andere Felder derselben Speicherstruktur beschädigt, lassen sich Sicherheitsgarantien in diesem Fall nicht prüfen.
In vielen Szenarien sind unsafe-Markierungen in nativen Rust-Programmen notwendig. In der allgemeinen Standardbibliothek, die Rust nutzt, enthalten rund 30 % der Pakete auf crates.io unsafe{}-Konstrukte. Auch hier kann der Compiler die Betriebssicherheit nicht vollständig prüfen.
Darüber hinaus gibt es Fälle, in denen Code ohne „unsafe“-Markierung dennoch Laufzeitprobleme verursacht. Das Rust Book (die offizielle Dokumentation der Sprache) listet einfache Beispiele wie Out-of-Bounds-Zugriffe auf. Solcher Code kompiliert zwar, führt jedoch bei bestimmten Werte-Kombinationen im Betrieb zum Absturz des Rust-Threads. Solche Ereignisse sind in sicherheitskritischen Anwendungen wie in der Automobilbranche höchst unerwünscht.
Laufzeitfehler in Rust
Dass Laufzeitfehler dennoch in Rust-Programme gelangen können, heißt nicht, dass diese nicht schon vor der Kompilierung entdeckt werden können. Es existieren formale Verifikations- und mathematische Analysemethoden, die Code bereits vor der Ausführung untersuchen. Sie können feststellen, ob Speicherfehler wie Buffer Overflows oder Null-Pointer-Zugriffe auftreten.
Solche statischen Analysen sind umfassend in einer Weise, wie es selbst umfangreiche dynamische Tests nicht sind. Korrekt angewandt garantieren sie null False Negatives (Fehler im Code bleiben unentdeckt) und nur wenige False Positives (Fehler angezeigt, obwohl keiner existiert). Mit diesen Methoden können Entwickler Probleme in Rust-Code identifizieren und zusätzliche Sicherungen einbauen, um den fehlerfreien Betrieb der Software sicherzustellen.
Neue Ansätze in der Softwareentwicklung
Insgesamt verlangt der Trend der Automobilindustrie hin zu Elektrifizierung und Konnektivität neue Ansätze in der Softwareentwicklung. Rust sticht hier als Sprache hervor, die das Risiko kostspieliger Rückrufe durch Softwarefehler reduziert, sichere Over-the-Air-Updates und Ferndiagnosen ermöglicht und zugleich die Fähigkeiten und Produktivität von Softwareteams modernisiert.
Da immer mehr Automobilunternehmen zum Rust-Ökosystem beitragen, wird die Sprache in den Fahrzeugen von morgen eine zunehmend wichtige Rolle spielen.
Doch auch wenn Rust hilft, das Risiko versehentlicher Fehler zu verringern, beseitigt es dieses nicht. Rust wird auf absehbare Zeit neben C und C++ bestehen. Und auch rein in Rust geschriebene Codebasen ohne unsafe-Blöcke sind nicht völlig immun gegen Speicherprobleme.
Rust-freundliche Werkzeuge
Die gute Nachricht: Rust-freundliche Werkzeuge wie der TrustInSoft Analyzer von TrustInSoft – ein statisches Analysetool, das formale Methoden nutzt, um mathematisch beweisbare Garantien für Software-Sicherheit und -Zuverlässigkeit zu liefern – können sämtliche dieser Probleme vollständig identifizieren, unabhängig davon, ob sie in nativem Rust-Code oder in Aufrufen von C/C++-Code auftreten. Noch besser: Der TrustInSoft Analyzer erkennt diese schwer auffindbaren Fehler auch in gemischten Codebasen und erhöht dadurch die Präzision der Analyse bei gleichzeitig weniger False Positives. Mit Hilfe formaler Methoden reduziert der TrustInSoft Analyzer den Bedarf an umfangreichen manuellen Tests und stellt sicher, dass kombinierte Automotive-Codebasen frei von unsicheren Speicherproblemen sind – lange bevor sie auf die Straße kommen. (na)
Der Beitrag beruht auf Unterlagen von TrustInSoft.