1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Cpp2Rust ist ein syntaxgesteuerter Übersetzer auf Basis des clang-AST, der C++-Eingaben automatisch in vollständig sicheren Rust-Code umwandelt
  • Beim Übersetzen wird eine C++-Datei mit clang geparst, daraus ein AST erstellt, der AST durchlaufen und Rust-Code als String erzeugt; anschließend gibt rustfmt eine einzelne .rs-Datei aus
  • Standardmäßig wird ein Reference-Counting-Modell verwendet; für Debugging und Performance-Vergleiche kann mit --model=unsafe auch unsafe Rust erzeugt werden
  • Der generierte Code hängt von der Runtime-Bibliothek libcc2rs ab; C-Pointer werden in Ptr<T> umgewandelt, das null, Arithmetik und Aliasing modelliert
  • Für die Übersetzung ganzer Programme ist compile_commands.json erforderlich; bei CMake-Projekten kann sie mit dem Flag CMAKE_EXPORT_COMPILE_COMMANDS=ON erzeugt werden

Automatische Umwandlung von C++ in sicheres Rust

  • Cpp2Rust ist ein Tool, das C++ automatisch in vollständig sicheres Rust übersetzt
  • Es ist ein syntaxgesteuerter Übersetzer auf Basis des AST von clang
  • Der Übersetzungsalgorithmus wird im auf der PLDI 2026 veröffentlichten Paper Cpp2Rust: Automatic Translation of C++ to Safe Rust beschrieben

Übersetzungs-Pipeline

  • Die eingegebene C++-Datei wird zunächst mit clang geparst, um einen AST zu erzeugen
  • Anschließend wird der AST durchlaufen und Rust-Code als String ausgegeben
  • Bei Bedarf werden Aufrufe der Runtime-Bibliothek libcc2rs eingefügt
    • Ein Beispiel ist die Behandlung von raw pointer semantics
  • Abschließend wird der Rust-Code mit rustfmt in eine einzelne .rs-Datei formatiert

Sicheres Rust und unsafe-Rust-Modell

  • Das Standardverhalten verwendet ein Reference-Counting-Modell und erzeugt vollständig sichere Rust-Ausgaben
  • Ein Generator für unsafe Rust wird ebenfalls bereitgestellt
    • Das Kommandozeilenargument lautet --model=unsafe
    • Zweck sind Debugging und Performance-Vergleiche

Runtime-Bibliothek libcc2rs

  • Der generierte Code hängt von einer Runtime-Bibliothek ab, die entwickelt wurde, um den Übersetzungsprozess zu vereinfachen
  • C-Pointer werden in den von libcc2rs bereitgestellten Typ Ptr<T> umgewandelt
  • Ptr<T> modelliert die Semantik von C-Pointern
    • null
    • Pointer-Arithmetik
    • Aliasing
  • Der Borrow Checker von Rust wird durch geprüfte Runtime-Operationen zufriedengestellt

Build- und Ausführungsablauf

  • Ein Beispiel für Ubuntu-Abhängigkeiten umfasst libclang-22-dev, clang++-22, ninja-build, cmake und ruff
  • Der Build erfolgt in der Reihenfolge cmake -GNinja .., ninja, ninja check
  • Der Befehl zur Übersetzung einer einzelnen Datei hat folgende Form
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs
  • Um unsafe Rust zu erzeugen, wird es wie folgt ausgeführt
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs --model=unsafe

Minimalbeispiel und Übersetzung ganzer Programme

  • Das Beispiel printf("hello world\n") in hello.cpp wird in Code umgewandelt, der Rusts println!("hello world") enthält
  • Die umgewandelte Datei hello.rs wird wie folgt kompiliert und ausgeführt
rustc hello.rs -L ../libcc2rs/target/debug
./hello
  • Für die Übersetzung ganzer Programme ist compile_commands.json erforderlich
  • In CMake wird compile_commands.json mit folgendem Flag erzeugt
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
  • Der Befehl zur Übersetzung eines ganzen Programms hat folgende Form
./build/cpp2rust/cpp2rust --dir=<dir> -o <output>.rs
  • <dir> muss das Verzeichnis sein, das compile_commands.json enthält

Testbefehle

  • Die gesamte Testsuite wird mit ninja check ausgeführt
  • Unit-Tests werden mit ninja check-unit ausgeführt
  • Unit-Tests für libcc2rs werden mit ninja check-libcc2rs ausgeführt
  • Unit-Tests für libcc2rs-macros werden mit ninja check-libcc2rs-macros ausgeführt
  • Um nach beabsichtigten Änderungen die erwartete Ausgabe neu zu generieren, wird REPLACE_EXPECTED=1 ninja check-unit verwendet

1 Kommentare

 
GN⁺ 3 시간 전
Kommentare auf Lobste.rs
  • Ich habe im Februar dieses Jahres einen Vortrag von Dan Wallach gehört, in dem er das DARPA-Projekt TRACTOR (Translating All C to Rust) vorgestellt hat [1] [2] [3]
    Die Tests und die Evaluation des Forschungsprogramms werden offenbar von einem Team des MIT Lincoln Laboratory durchgeführt
    Soweit ich weiß, ist es noch in der Forschungsphase, aber ich interessiere mich sehr für dieses Thema und bin ziemlich gespannt, welche Tools und Ideen daraus entstehen werden
    Es scheint tatsächlich viele „kleine“ Bugs beseitigen zu können, die aus der Speicherverwaltung entstehen
    [1] https://www.darpa.mil/research/programs/translating-all-c-to-rust
    [2] https://github.com/DARPA-TRACTOR-Program
    [3] https://ll.mit.edu/r-d/projects/…

  • Interessant. Es wäre wirklich schön, ein oder zwei Bibliotheks- oder App-Demos zu sehen, die die Autoren damit konvertiert haben und die sie selbst für interessant halten

  • Mir ist nicht ganz klar, warum der Konvertierungsschritt nötig ist. Wenn genug Informationen vorhanden sind, um in speichersicheres Rust zu konvertieren, hat man dann nicht auch genug Informationen, um per statischer Analyse zu garantieren, dass das C++ sicher ist?

    • Bei der Konvertierung werden neue Prüfungen eingefügt; es ist also weniger „der ursprüngliche Code war sicher“, sondern eher der Kompromiss: „Der neue Code beendet sich bei Problemen mit einem lauten Fehler, statt den Speicher zu beschädigen“
    • Das steht gleich am Anfang des im README verlinkten Papers
      Etwa 70 % der Sicherheitslücken in weit verbreiteter Software entstehen demnach durch Memory-Safety-Bugs in Sprachen wie C und C++; trotz jahrzehntelanger Investitionen in Gegenmaßnahmen wie statische Analyse, Sanitizer und Hardware-Isolation nutzen Angreifer weiterhin unsichere Speicheroperationen aus
      Als langfristig vielversprechende Lösung wird beschrieben, bestehende C++-Codebases in speichersichere Sprachen wie Rust zu migrieren, was manuell jedoch übermäßig teuer und fehleranfällig sei
      Cpp2Rust behauptet, das erste System zu sein, das C++-Programme automatisch in funktional äquivalenten und speichersicheren Rust-Code übersetzen kann
      Dabei wird ein Teil der Performance gegen Sicherheit eingetauscht: Die grundlegende Diskrepanz zwischen den uneingeschränkten Alias-Referenzen von C++ und Rusts Ownership-Modell wird durch eingefügte Laufzeitprüfungen für Ownership und Mutability gelöst, wodurch die Semantik erhalten und zugleich Sicherheit garantiert werden soll
      Um den Overhead der dynamischen Prüfungen zu senken, wurden außerdem Source-to-Source-Optimierungen für Rust-Code entwickelt, die redundante Ownership-Operationen entfernen und einen beträchtlichen Teil der verlorenen Performance zurückholen sollen
      Die Motivation ist also nicht einfach, Sicherheit zu beweisen, sondern eher, nach Rust zu portieren und die weitere Entwicklung in Rust zu erleichtern
    • Es gibt nicht genug Informationen, und nach Rice’s Theorem wird es sie auch künftig nicht geben
      Die Idee solcher Projekte ist, einen Ausgangspunkt für weiteres Refactoring in idiomatischeres Rust zu schaffen; dadurch kann der Code analysierbar werden
      Hoffentlich lassen sich dann im konvertierten Code auch Laufzeitprüfungen oder unsichere Teile entfernen
      Man könnte sagen, man könne C++ doch genauso in eine „Rust-Form“ refaktorisieren und damit analysierbar machen; C++ hat aber andere Semantiken, die mehr Flexibilität erlauben, wodurch die zu analysierenden Pfade exponentiell zunehmen können oder die statische Analyse in unklaren Sackgassen landet
      Man müsste versprechen, nichts zu tun, was statische Analysewerkzeuge vor schwierige Aufgaben stellt, und keine Grenzfälle auszunutzen; dafür könnte man einen C++-Dialekt mit eingeschränkterer Semantik und zusätzlichen Annotationen schaffen
      Circle/Safe C++ hat gezeigt, dass das möglich ist, aber die C++ WG hat diese Richtung deutlich abgelehnt; damit bleibt nur, selbst einen nicht unterstützten, Rust-ähnlichen C++-Dialekt zu verwenden oder einfach Rust zu nutzen
  • Was genau passiert, wenn der Eingabecode selbst unsicher ist?

    • Das wird über Laufzeitprüfungen behandelt. Bei einer Verletzung kann das Programm daher sicher und deterministisch crashen, statt den Speicher zu beschädigen