- Der Maßstab, Rust allein wegen der Existenz von
unsafeauszuschließen und nur Fil-C als sicher anzusehen, verfehlt den tatsächlichen Einsatzbereich von Software und technische Trade-offs - Fil-C wandelt fehlerhafte Speicherzugriffe in C/C++ in Panics um, bringt aber Kosten wie ABI-Inkompatibilität, in manchen Situationen ein Mehrfaches an Performance-Einbußen und die Einführung von GC mit sich
- In rund 5 Millionen Zeilen Rust-Code in Android wurde eine potenzielle Memory-Safety-Schwachstelle gefunden, die vor der Veröffentlichung behoben wurde; das entspricht geschätzt 0,2 Fällen pro Million Zeilen und liegt mehr als 1.000-mal niedriger als frühere C/C++-Daten von etwa 1.000 Fällen
- Man muss sich nicht zwischen einer Technik, die in allen Programmen 99,9 % der Probleme verhindert, und einer Technik, die in 90 % der Programme 100 % verhindert, entscheiden; für Software, bei der die Einschränkungen von Fil-C schwer akzeptierbar sind, eignen sich Alternativen wie Rust
- Memory Safety muss zusammen mit Performance, ABI, GC und der Vermeidung von Data Races betrachtet werden; wer Rust als unzureichend kritisiert, sollte mindestens denselben Maßstab an normales C/C++ und Nicht-Fil-C-Zig anlegen
Rust und das Verantwortungsmodell bestehender Systemsprachen
- Die Diskussion über Memory Safety in nicht-GC-basierten Systemprogrammiersprachen drehte sich bislang vor allem um die Unterschiede im Verantwortungsmodell von Rust und C/C++/Zig
- Rust nimmt die Kosten in Kauf, auch manche Programme abzulehnen, die sicher sein könnten, und versucht dafür, die Kompilierung von Programmen zu verhindern, die Memory-Safety-Probleme verursachen können
unsafeist ein Ausweg, der unter anderem das Dereferenzieren roher Pointer erlaubt und damit einige Garantien umgeht- Sprachen der C-Familie überlassen Memory-Safety-Garantien größtenteils den Programmierern
- Zwar gibt es je nach Sprache unterschiedliche Unterstützungsniveaus, etwa RAII und Smart Pointer in C++ oder
deferin Zig, doch fehlerhafte Speicherzugriffe werden damit grundsätzlich nicht blockiert
Die zusätzliche Option durch Fil-C
- Fil-C bietet einen neuen Ansatz, um C- und C++-Code memory-safe auszuführen
- Bei fehlerhaften Speicherzugriffen wie Out-of-bounds-Zugriffen oder Use-after-free wird eine Panic ausgelöst
- Es kombiniert GC mit InvisiCaps, die den Speicher verfolgen, auf den Pointer zugreifen können
- Auch für Zig wurde ein neuer Kompilierungsmodus vorgeschlagen, der von Fil-C inspiriert ist
- Wenn einige populäre C/C++-Projekte mit Fil-C kompilierte Releases anbieten, könnten mehr Optionen entstehen, um Memory-Safety-Schwachstellen zu reduzieren
Der Maßstab, nach dem Rust als unsicher gilt
- Der Fil-C-Entwickler hat Rust auf Twitter als nicht memory-safe Sprache bewertet, weil sich mit
unsafeeinige Garantien umgehen lassen - Auch Zig-Entwickler Andrew Kelley bezeichnete in einem zugehörigen Issue-Titel einen von Fil-C inspirierten Modus als „im Gegensatz zu Rust tatsächlich memory-safe“ Kompilierungsmodus
- Manche Diskussionen fordern, dass Rust-Nutzer, wenn ihnen Memory Safety wirklich wichtig sei, Rust aufgeben und das sicherere Fil-C bewerben sollten
- Dieser Maßstab vergleicht Rust und Fil-C, ohne die realen Kosten von Fil-C einzubeziehen, und ähnelt damit der Haltung, für die die Rust-Community häufig als fanatisch kritisiert wird
Einschränkungen beim Einsatz von Fil-C
- Fil-C ist kein kostenloser Drop-in-Ersatz
- Es ist nicht ABI-kompatibel mit Programmen, die nicht mit Fil-C kompiliert wurden
- In manchen Situationen kann es um ein Mehrfaches langsamer sein
- Es führt GC ein
- Für Programme wie einfache Utilities, bei denen Performance-Einbußen kaum spürbar sind oder kein dynamisches Linking nötig ist, müssen diese Einschränkungen nicht entscheidend sein
- Umgekehrt gibt es viele populäre Projekte, die GC und ABI-Inkompatibilität nicht akzeptieren können; Programme, auf die Fil-C in der aktuellen Form schwer anwendbar ist, passen oft gut zu Rust
Schwachstellendaten aus realem Rust-Code
- Es gibt noch nicht viele Daten, um die praktische Sicherheit von Rust zu beurteilen, aber in Rust-Software wurden bislang nicht viele ausnutzbare Memory-Safety-Schwachstellen gefunden
- In mehr als 5 Millionen Zeilen Rust-Code in Android wurde eine potenzielle Memory-Safety-Schwachstelle gefunden und vor der Veröffentlichung behoben
- Die geschätzte Schwachstellendichte liegt bei 0,2 Fällen pro Million Zeilen
- Frühere C/C++-Daten von Android liegen bei etwa 1.000 Fällen pro Million Zeilen
- Die Dichte im Rust-Code wird als mehr als 1.000-mal niedriger als bei C/C++ verfolgt
- Die Zahlen können je nach Projekt variieren, liefern aber einen Beleg dafür, dass Rust in realen Umgebungen das Risiko der Einführung von Memory-Safety-Problemen stark senkt
Warum man nicht nur eines wählen muss
- Der hypothetische Vergleich zwischen einer Technik, die in allen Programmen 99,9 % der Probleme verhindert, und einer Technik, die in 90 % der Programme 100 % der Probleme verhindert, zeigt, dass sowohl Einsatzbereich als auch Präventionsgrad wichtig sind
- Die tatsächlichen Anteile sind unbekannt, aber man muss nicht nur einen der beiden Ansätze wählen
- C/C++/Zig-Projekte, die die Trade-offs akzeptieren können, können Fil-C-Binaries anbieten
- Software, die Fil-C nicht verwenden kann, kann in Sprachen geschrieben werden, die das Risiko von Memory-Safety-Schwachstellen vollständig oder größtenteils eliminieren
Warum man Rust wählt, obwohl GC möglich wäre
- Es ist vertretbar, Rust zu verwenden, auch wenn GC-basierte Optionen wie Go oder Fil-C existieren
- Programme, die in einer GC-basierten Sprache geschrieben werden können, benötigen häufig kein
unsafe; Programme, dieunsafebenötigen, können häufig keinen GC verwenden - Andere Sprachgarantien und Features wie Data-Race-Vermeidung können höher bewertet werden als ein kleines Memory-Safety-Risiko
- Fil-C wandelt Memory-Safety-Schwachstellen in bestehendem C/C++ in Crashes um
- Das ist besser als Sicherheitslücken, doch wenn die historische Dichte von etwa 1.000 Fällen pro Million Zeilen unverändert bleibt, bleiben viele zu behebende Crashes übrig
- In der Vergangenheit gab es auch Sicherheitslücken, bei denen Angreifer die Fähigkeit ausnutzen konnten, ein Programm zum Absturz zu bringen
Ein konsistenter Maßstab für Memory Safety
- Wenn selbst 0,2 Fälle pro Million Zeilen bei Rust nicht akzeptabel sind, muss dieselbe oder eine strengere Kritik auch auf normales C/C++ und Nicht-Fil-C-Zig angewendet werden
- Alternativen mit geringerer Sicherheit als Rust zuzulassen, Rust aber allein wegen
unsafeauszuschließen, wendet Memory-Safety-Absolutismus nicht konsistent an
1 Kommentare
Lobste.rs-Meinungen
Die Aussage von Andrew Kelly, die der OP offenbar als unangenehm aufgenommen hat, besagt, dass Zig zu einem vollständig speichersicheren Executable kompilieren kann, ohne Ausweg selbst bei vollständigen C/C++-Abhängigkeiten, wobei die Performance-Kosten je nach Häufigkeit des Pointer-Trackings etwa beim 1- bis 6-Fachen liegen
Der Autor versteht das als Angriff auf Rust und greift am Ende des Beitrags Zig an, aber der neue Build-Modus von Fil-C und Zig ist ein positiver Beitrag für das Ökosystem und bietet andere Designpunkte und Trade-offs als Rust
Ich interpretiere es so, dass sich die Performance-Kosten auf nahezu das 1-Fache reduzieren lassen, wenn man dem vom Zig-Team bevorzugten datenorientierten Programmierstil folgt
Dass man das als unnötig provokant liest, ist nicht unvernünftig, und Andrew hat den Titel später weniger reißerisch formuliert
Ungewöhnlich war dieses Mal, dass der leichte Spott „eure Sprache ist nicht speichersicher“ Rust traf, und das Ausmaß der Reaktion der Rust-Entwickler war beträchtlich
Ich mag Rust auch sehr, aber man sollte das fair aufnehmen
Noch sicherer ist darüber hinaus das formal verifizierte C von seL4
Zig hat den Vorteil, dass man bei fehlgeschlagener Speicherallokation leicht korrekt beenden kann und dass es schnell kompiliert
Ich weiß nicht, ob Fil-C speichersicherer als Rust ist, aber für meine Anwendungsfälle sind Garbage Collector und Inkompatibilität mit dem C ABI entscheidende Hindernisse
Man kann Race Conditions vermeiden, wenn man alles Single-Threaded baut, und Speichersicherheit durch einen Garbage Collector bekommen, aber ich mag an Rust, dass es beides ohne diese zwei Kompromisse bietet
Da mir Speichersicherheit sehr wichtig ist, sind Fil-C und Zigs Fil-C-ABI-Implementierung eine naheliegende Wahl
Bei Rust-Projekten mit C-Abhängigkeiten werden die Sicherheitsgarantien schwächer, und nur reines Rust zu verwenden ist zwar möglich, aber unbequem
Auch Rust sollte das Fil-C ABI implementieren können, um C-Abhängigkeiten sicher zu bauen und mit Rust zu verbinden; ich verstehe nicht, warum das kontrovers sein sollte
Wenn man ähnliche Funktionen implementiert, würde ich mir wünschen, dass sie nur als Hilfsmittel zur Verbesserung von FFI-Code in Debug-Builds genutzt werden
Ein Garbage Collector plus Laufzeitprüfungen für alle Operationen passt nicht zu allen Einsatzfällen
Bevor man Fil-C als bloßes Hilfsmittel abtut, sollte man sich den Fil-C-Vortrag auf der Software Should Work Conference ansehen
Der Vortragende präsentierte auf einem Linux-Laptop, dessen kompletter Userspace bis hin zu OpenOffice Impress aus Fil-C bestand
Für manche C/C++-Programme ist es vielleicht ungeeignet, aber es wirkt nicht wie eine Spielzeugtechnologie, als die der Autor es ansieht
Da ich aus Python komme, war Speichersicherheit für mich eine Grundannahme, und ich habe Rust aus drei Gründen gewählt
Erstens kann man mit dem starken Typsystem Korrektheit zur Compile-Zeit absichern, und die mit
#![forbid(unsafe_code)]und cargo-geiger durch Dependency-Audits erreichte Speichersicherheit ist davon der am wenigsten interessante AusdruckZweitens bietet es ein Ökosystem, in dem sich einmal sicher geschriebener Code leicht über viele Sprachen und Laufzeitumgebungen hinweg teilen lässt
Drittens gibt es Syntax-Sugar wie das damalige
try!(x), das das Schreiben von High-Level-Code angenehm machtZig und Fil-C scheinen die Fähigkeit nicht zu erfüllen, logische Fehler vom Compiler finden zu lassen, indem man Invarianten über Typzustandsmuster oder Newtypes ins Typsystem kodiert
Auch die ABI-Inkompatibilität von Fil-C ist ein Problem, wenn man sicher kompilierte Module für bestehende Laufzeitumgebungen wie CPython auf Shared Web Hosting schreiben will
comptimeausdrucksstärker als RustEs gibt Trade-offs bei Compile-Zeit und Ausführlichkeit, und der Großteil des Ökosystems geht nicht so weit, aber möglich ist es tatsächlich und ziemlich interessant
Ich frage mich, warum eine Technologie wie Fil-C nicht schon vor 20 Jahren aufkam
Aber niemand wollte den Preis dafür in CPU oder Speicher, also in Kosten, bezahlen
Von 2004 bis 2018 gab es zwar die Idee, aber die Vorstellung von speichersicherem C galt an sich als töricht; von 2018 bis 2023 änderte sich diese Sicht, aber es fand sich kein Weg zu extremer Kompatibilität
Das frühe Fil-C von 2023 bis 2024 hatte deutlich schlechtere Kompatibilität und Performance, und erst der Durchbruch von InvisiCaps Ende 2024 brachte die heutige hohe Kompatibilität und ordentliche Performance
Der Auslöser für den Sinneswandel um 2018 war die Beobachtung, dass die auf GPUs verwendeten C-Varianten eine einfache Form von speichersicherem C sind
Wenn man die Aussage „Wenn dem Rust-Lager Speichersicherheit wirklich wichtig ist, sollte es das sicherere Fil-C unterstützen und Rust aufgeben“ maximal wohlwollend auslegt, dann heißt das: Jetzt, da es Fil-C gibt, sollte man aufhören, die Welt in Rust neu zu schreiben, zu C/C++ zurückkehren und das alte integrierte Bibliotheksökosystem erhalten
Selbst wenn sich Rust-Bibliotheken aus C/C++ nutzen lassen, gibt es Entwickler, die das nicht wollen; wenn Rust-Nutzer also die bessere Lösung anerkennen und aufgeben, würde die Spaltung verschwinden
Aber Fil-C bringt Trade-offs mit, die Rust nicht hat, nämlich Garbage Collector und Unterstützung nur für x86-64 Linux
Neben der Speichersicherheit sind auch Cargo und das Fehlen eines globalen Namensraums wichtige Gründe für Rust, und es ist bedauerlich, dass die Spaltung zwischen Sprachlagern mit einem breiteren Kulturkampf verknüpft ist
Ich will einfach nur Bibliotheken bauen, die jeder gern verwendet
Unter C-Entwicklern gibt es vielleicht Leute, die keine C++-Bibliotheken wollen, und mit Zig und Odin ist weitere Fragmentierung hinzugekommen, sodass sie auch ohne Rust bliebe
Ich frage mich, ob Rust wirklich eine besondere Art von Fragmentierung erzeugt
comptimeerhältRust kodiert mehr Constraints und ist daher als Ursprungssprache ideal