Cpp2Rust: Automatic Translation of C++ to Safe Rust
31 points by gnyeki
31 points by gnyeki
I attended a talk by Dan Wallach back in February this year. He mentioned a DARPA project called TRACTOR (Translating All C to Rust) [1] [2] [3].
Test and evaluation of the research program is being performed by a team at MIT Lincoln Laboratory.
They're still researching it as far as I know, but there's a lot of interest in this topic. I am quite interested to see how such tools and ideas turn out - it would really eliminate a lot of "trivial" bugs that come with memory management.
[1] https://www.darpa.mil/research/programs/translating-all-c-to-rust
[2] https://github.com/DARPA-TRACTOR-Program
[3] https://www.ll.mit.edu/r-d/projects/translating-all-c-rust-tractor-benchmarks
I'm intrigued. It'd be really cool to see a demo of a library or an app or two that the authors translated with this, and consider interesting.
https://github.com/Cpp2Rust/cpp2rust-testsuite contains examples from the associated paper with versions of WOFF2, brotli, and some others.
Looking at say https://github.com/Cpp2Rust/cpp2rust-testsuite/blob/master/woff2/out/refcount/src/bin/woff2_compress.rs it may be safe Rust, but it's certainly not idiomatic Rust.
Thanks. That's funny... I looked here:
https://github.com/Cpp2Rust/cpp2rust/tree/master/tests
before I asked, thinking they may have been in the test suite. I just didn't think to look elsewhere for the test suite, and the README's "Test Suite" section didn't mention that there was an external one.
I'm not sure I understand why the translation step is needed. Presumably if you have sufficient information to translate it into memory-safe Rust, then you have enough information to statically analyze it to ensure that the C++ is also safe?
This inserts new checks during translation, so it's less "original code was safe" and more "the new code will crash loudly rather than mess up the memory" trade.
The paper linked from the README has this information at the start:
About 70% of security vulnerabilities in widely deployed software originate from memory-safety bugs in languages such as C and C++. Despite decades of investment in mitigations, from static analysis and sanitizers to hardware isolation, attackers continue to exploit unsafe memory operations. A promising long-term solution is to migrate existing C++ codebases to memory-safe languages such as Rust, but doing so manually is prohibitively expensive and error-prone. In this paper, we present Cpp2Rust, the first system capable of translating C++ programs into functionally equivalent and memory-safe Rust code automatically. By trading some performance for security, Cpp2Rust addresses the fundamental mismatch between C++’s unrestricted aliasing and Rust’s ownership model by inserting runtime-enforced ownership and mutability checks, ensuring safety while preserving semantics. To mitigate the performance overhead of dynamic checks, we developed a suite of source-to-source optimizations for Rust code that eliminate redundant ownership operations and recover much of the lost performance.
So the motivation is to help with porting & moving future development to Rust, not just proving safety.
"About 70% of security vulnerabilities in widely deployed software originate from memory-safety bugs in languages such as C and C++."
I now code primarily in Rust, and have never written a meaningful amount of C++ (this is so that no one here thinks I'm a secret anti-memory safety shill).
I have now seen this "70% of security vulnerabilities ... memory-safety bugs" cited a lot ever since it was first reported years ago. The paper in this post refers to these 2019 slides from Microsoft (I think the relevant parts are page 10 onwards?), and a 2021 blog post from the Chromium developers at Google (via this page). The Chromium page is the one with the exact "70% security memory safety" number.
I think those two sources are ultimately always the only origin of the "70%". Has anyone tried to reproduce this number by looking at any other large projects in C and C++? e.g. Linux kernel or Firefox
Alex Gaynor did a survey of results on this question a few years ago, and found similar numbers reported across a wide range of C and C++ projects, including the Linux kernel and Firefox's CSS engine.
Thank you! That is pretty much what I was hoping to see. The (unweighted) mean of the eight projects cited in this blog post does come out to ~71.5%.
There are not a lot of very strong empirical results in the field of programming languages.
This is an eternal gripe, but this is probably as good as I can hope to get for that number.
curl attributes half of vulns to C-specific bugs (not exactly the same thing tho): https://daniel.haxx.se/blog/2021/03/09/half-of-curls-vulnerabilities-are-c-mistakes/
I don't think it's even widely disputed that in pointer-heavy languages with manual memory management majority of bugs will manifest in some kind of memory safety issue (people mainly argue that good programmers and tooling can find these bugs, or that runtime mitigations can make them unexploitable).
Even if the root cause is an application-level logic bug, the nearest weak point where it can break is likely to be memory management, buffer indexing, uninit memory or some other UB. These things only guarantee safety if the program is correct, so you need to be lucky to have incorrect logic in your program still perform correct memory management.
Thanks for linking that curl blog post (and for your response overall!)
I also have no doubts that a high number/majority of bugs in complex C/C++ projects are to do with memory management. I feel sceptical specifically of the widely given "70%" when it's used to drive real arguments, or especially as a basis for papers like the one here. It is a nice round number, but without replication, that specific number can be clearly attributed just to Chromium's codebase. edit: see sibling response from @nelhage.
You don't have sufficient information, and Rice's theorem says you won't.
The idea behind these projects is to make a starting point for further refactoring into idiomatic Rust, and that will make the code analyzable (and hopefully allow to remove runtime checks or unsafety from the converted code).
Why not refactor C++ to be "Rust-shaped" and equally analyzable? Because C++ has different semantics which allow more flexibility, creating either exponentially more paths to analyze or outright ambiguous dead-ends for static analysis. You need to promise to a static analyzer that you won't do the tricky things and won't exploit edge cases. For that, you might create a C++ dialect with more restricted semantics and extra annotations. Circle/Safe C++ demonstrated it's possible, but since the C++ WG firmly rejected that direction, you're left with your own unsupported Rust-like-C++ dialect, or can just use Rust.
Rice's theorem is irrelevant here. It applies equally to programs written in Rust.
It doesn't equally apply (there's always a limit but the limit isn't in the same place), because Rust forbids the hardest-to-analyze code patterns. This "fighting with the borrow checker" is the fight against Rice's theorem.
Rust's borrowing is very picky about mutable aliasing, while in C++ you don't need to go far to find massively hard cases like begin/end pointers in iterators. Circle/Safe C++ documented in detail all the things that needed to be removed to make the analysis at Rust's level tractable.
This is a cute paper, although I hate the comparisons -- I don't find it very interesting to see how their optimized cpp2rust tool compared to their own work-in-progress unoptimized version!
PL papers do this a lot, and it always irks me... I would have much rather liked to see performance comparisons against tools in a similar problem space, particularly Fil-C. I'm disappointed to not see it mentioned at all in the related work. My understanding is that Fil-C has heavy memory overhead but (likely) better performance... and of course isn't a workable start for a Rust port... not sure what else.