Boffins carve up C so code can be converted to Rust
- Reference: 1735907591
- News link: https://www.theregister.co.uk/2025/01/03/mini_c_microsoft_inria/
- Source link:
The C programming language was [1]created in the early 1970s and has been used to build numerous critical systems, applications, and libraries. The Linux kernel, for example, is written mostly in C.
But C, like its extension C++, is not designed for memory safety. It features manual memory management, which is more efficient and flexible than automated memory management (e.g., garbage collection), but also more prone to memory errors.
[2]
[3]Memory safety errors like out-of-bounds reads and writes and use-after-free represent the majority of software vulnerabilities in numerous recent studies. In Google's Android operating system, they accounted for [4]76 percent of vulnerabilities in 2019 , a figure that the Chocolate Factory projected would fall to 24 percent by the end of 2024 through the use of Rust and [5]Safe Coding practices.
[6]
[7]
Rust code can be written to be either memory safe or [8]unsafe , at the discretion of the developer. While C and C++ code can be made more memory safe through diligence, static analysis, and testing, neither programming language offers memory safety guarantees out of the box.
For the past few years, there's been an industry and government-backed effort to reduce the use of C and C++ code and increase the use of memory safe programming languages like Rust, Go, Python, and Java (though these may link to unsafe libraries). As the Internet Security Research Group's (ISRG) Prossimo Project puts it: "Using C and C++ is bad for society, bad for your reputation, and it's bad for your customers."
[9]
Not everyone would go quite that far. Many C and C++ programmers would rather find ways to keep using their favored tools without jumping on the Rust bandwagon, and even Google, among the more vocal Rust evangelists, has acknowledged that C and C++ code [10]will be around for a long time .
So there's been quite a bit of effort to develop ways to make C and C++ more memory safe and to develop automated code conversion mechanisms like [11]DARPA's TRACTOR program.
Efforts to make C memory safe like [12]TrapC and [13]Fil-C come with trade-offs. TrapC, still in development, focuses on a subset of the language. Fil-C currently imposes a performance hit and lacks full Application Binary Interface (ABI) compatibility.
[14]Rust haters, unite! Fil-C aims to Make C Great Again
[15]To kill memory safety bugs in C code, try the TrapC fork
[16]The US government wants developers to stop using C and C++
[17]CISA boss: Makers of insecure software must stop enabling today's cyber villains
In a preprint paper titled " [18]Compiling C to Safe Rust, Formalized ," authors Aymeric Fromherz (Inria) and Jonathan Protzenko (Microsoft) provide an alternative to automatic C-to-Rust [19]conversion [20]schemes that produce [21]unsafe Rust. Their focus is on providing a conversion path for industrial-grade code that's been [22]formally verified .
"C allows programmers to be creative with aliasing, low-level casts, memory management, and data representation," the authors explain. "Expressing those patterns in Rust requires opting out of many static guarantees, in order to allow unchecked aliasing, casts between representations (a.k.a. 'transmutation' in Rust lingo), and so on, which is done through the unsafe feature of Rust. But doing so obliterates the benefits of Rust!"
[23]
So Fromherz and Protzenko have developed a subset of C, dubbed "Mini-C," which avoids common C patterns and features like pointer arithmetic and implicit mutability that just can't be translated directly to safe Rust.
Implementing Mini-C using the [24]KaRaMeL compiler framework, the boffins say their approach produces safe Rust code, with the caveat that some refactoring may be required.
"Instead of automatically translating C in its full generality to unsafe Rust and attempting to make the resulting code safer, we rather target a data-oriented, applicative subset of C," they explain. "Our translation process is therefore semi-active: users may have to perform minimal adjustments to the source C program to fit the supported language subset; once in this subset, our approach then automatically produces valid, safe Rust code."
They tested the conversion process on HACL* (High-Assurance Crypto Library), a verified cryptographic library consisting of 80,000 lines of C code, and on EverParse, a verified formatters and serializers library consisting of 1,400 lines of C.
The HACL* conversion required minimal code changes and the EverParse conversion worked without source alteration. And the results worked well, they claim – the Rust code exhibited the same performance profile as the original C code, despite the addition of [25]fat pointers and runtime bounds checks.
This work is now helping to make a variety of dependent applications a bit more secure. The authors say their Rust-compiled HACL* has been packaged within the libcrux cryptographic library, parts of which have been added to Mozilla's [26]NSS and OpenSSH. ®
Get our [27]Tech Resources
[1] https://www.bell-labs.com/usr/dmr/www/chist.html
[2] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=2&c=2Z3gXtReb0I4Tip_FruDZ1wAAAAk&t=ct%3Dns%26unitnum%3D2%26raptor%3Dcondor%26pos%3Dtop%26test%3D0
[3] https://www.memorysafety.org/docs/memory-safety/
[4] https://www.theregister.com/2024/09/25/google_rust_safe_code_android/
[5] https://dl.acm.org/doi/10.1145/3651621
[6] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=4&c=44Z3gXtReb0I4Tip_FruDZ1wAAAAk&t=ct%3Dns%26unitnum%3D4%26raptor%3Dfalcon%26pos%3Dmid%26test%3D0
[7] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=3&c=33Z3gXtReb0I4Tip_FruDZ1wAAAAk&t=ct%3Dns%26unitnum%3D3%26raptor%3Deagle%26pos%3Dmid%26test%3D0
[8] https://doc.rust-lang.org/std/keyword.unsafe.html
[9] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=4&c=44Z3gXtReb0I4Tip_FruDZ1wAAAAk&t=ct%3Dns%26unitnum%3D4%26raptor%3Dfalcon%26pos%3Dmid%26test%3D0
[10] https://www.theregister.com/2024/10/16/google_legacy_code/
[11] https://www.theregister.com/2024/08/03/darpa_c_to_rust/
[12] https://www.theregister.com/2024/11/12/trapc_memory_safe_fork/
[13] https://www.theregister.com/2024/11/16/rusthaters_unite_filc/
[14] https://www.theregister.com/2024/11/16/rusthaters_unite_filc/
[15] https://www.theregister.com/2024/11/12/trapc_memory_safe_fork/
[16] https://www.theregister.com/2024/11/08/the_us_government_wants_developers/
[17] https://www.theregister.com/2024/09/20/cisa_software_cybercrime_villains/
[18] https://arxiv.org/abs/2412.15042
[19] https://github.com/immunant/c2rust
[20] https://gitlab.com/citrus-rs/citrus
[21] https://dl.acm.org/doi/10.1145/3510454.3528640
[22] https://www.moritz.systems/blog/an-introduction-to-formal-verification/
[23] https://pubads.g.doubleclick.net/gampad/jump?co=1&iu=/6978/reg_software/front&sz=300x50%7C300x100%7C300x250%7C300x251%7C300x252%7C300x600%7C300x601&tile=3&c=33Z3gXtReb0I4Tip_FruDZ1wAAAAk&t=ct%3Dns%26unitnum%3D3%26raptor%3Deagle%26pos%3Dmid%26test%3D0
[24] https://github.com/FStarLang/karamel
[25] https://doc.rust-lang.org/std/ptr/trait.Pointee.html#pointer-metadata
[26] https://firefox-source-docs.mozilla.org/security/nss/index.html
[27] https://whitepapers.theregister.com/
Re: “Minimal adjustments”
And if they can automate those adjustments, why can't that automation be incorporated directly into the C compiler?
That way not only would you eliminate the memory safety issues of C, you'd eliminate RUST too.
I'd call that win-win.
What's the point?!
So they turned C into not-C so they could then turn the not-C into Rust? So basically getting you to do all the heavy lifting of porting and/or adapting the existing code to the subset that already aligns with Rust to make the process *seem* easier?
PS. Those other quotes about how an existing working solution is apparently the devil and all should migrate to the new One True Path remind me of the equally fanatical laser-eyed Bitcoiners. Yes your solution has a place but will you please shut up preaching about it? It's not exactly perfect either.
Re: What's the point?!
Another glorious Rust victory for converting "Hello, world" C programs everywhere.
Re: What's the point?!
Wouldn't it be simpler to convert it into FORTRAN?
FORTRAN is very easy to program, very fast, parallelisable (yes spellcheck, that is a word) and memory safe
Why?
1. So are they expecting programmers to write programs in mini-C, so that those programs can be translated to safe Rust? Why wouldn't we write those programs in safe Rust and just skip the writing-in-C part?
2. By selecting a "safe" subset of C, you remove the advantages of using C in the first place. "With great power comes great responsibility", etc.
3. If people want to use a "safe" language, why aren't they using Ada?
Re: Why?
"Why wouldn't we write those programs in safe Rust and just skip the writing-in-C part?"
Because you have an existing code base represent $hours work and you want to convert it rather than scratch build it.
But that's the wrong question. The right question is what does Rust bring to the party when you already have code that, as I follow it, is mathematically validated and is written to such a narrow standard that it can go through this with few alterations? You want Rustification to reveal bugs and make it less likely those bugs re-occur in the future. But that means ordinary code, which means pointer-arithmetic and type-punning and all those other "unsafe" things ordinary C will do.
Re: Why?
>The right question is what does Rust bring to the party
Simple, you re-write your C code so that it doesn't use any dynamic memory allocation or raw pointers. It can then be converted into RUST, which is memory safe because it doesn't use any dynamic memory allocation or raw pointers.
Re: Why?
If one prefer a syntax that it's quite like C, I think that James Gosling had devised a solution in 1995, when working in Sun.
In Java the source of a lot of problems present in C are avoided using different data structures and hiding the pointer presence.
"You can't convert all C to Rust, so we made a crippled C that isn't compatible with C or Rust, but which you can then convert to Rust, and it doesn't work for all C".
Yeah, I don't really see the use case here.
How about this:
- If people are using pointer arithmetic, it's for a reason. And Rust won't be able to do a damn thing differently or any more securely about that.
- If you want people to write inherently safe code, you can't write it in C *at any point*. We have any number of languages for that.
- You cannot write safe code that interacts with devices, computer buses or the majority of hardware directly. It's just not possible. The only fix for that? To radically change hardware architectures to return only well-formed descriptive device identifiers with a set protocol for communicating with them (i.e. no memory addresses, no DMA, etc.).
Until we change all the BIOS/UEFI, PCIe etc. specifications to allow arbitrary device detection and communication at bus speed using only, say, JSON or XML or similar languages, you're still going to need to write those bits in C (as they have to make assumptions about what the underlying bits mean and directly access memory), and that means you can't write them memory-safely in any language at all, ever, whatsoever.
Down vote?
Can the down voter please elaborate? I've reread the post a couple of times and as far as I can tell it is spot on. Just asking because I'm curious.
Re: Down vote?
I didn't down vote, but the assertion low level code has to be written in C is false. I've written the kind of code described in ASM, Forth and C++. I presume you can write it in "unsafe" Rust. So C is not the only game in town.
And how does JSON or XML get transferred to the host? Is it an "unsafe" DMA or PIO? (Yes, I've written PIO code.) Whereas port based IO just looks like a function that takes or returns a value so that is intrinsically safe.
I'd've also thought a DMA could be made to appear safe - if we trust a kernel function which does the low level stuff so the device driver just makes a DMA() call in, presumably, the same way memory-mapped IO is made appear to be safe. But I don't know enough about modern IO or Rust to be sure on either case.
Re: Down vote?
The downvote wasn't me either, I don't downvote anyone's comments... you are all entitled to your opinion.
I agree with this bit :
"You cannot write safe code that interacts with devices, computer buses or the majority of hardware directly. It's just not possible."
However, I don't agree with the next part :
"The only fix for that? To radically change hardware architectures to return only well-formed descriptive device identifiers with a set protocol for communicating with them"
Because ultimately if you just push the problem down the stack towards the hardware then you just move the problem. A protocol would need to be implemented in some language, a firmware needs to be implemented in some language, even CPU microcode might at a push be a language of sorts, ok... maybe no longer the OS kernel's problem but still a problem.
I'd probably prefer a situation where a problem is resolvable by changing the software via an update than changing or reprogramming the hardware and we're already too far down the path of having mutliple 'firmwares' which are just bits of software running *outside* of the control of your OS anyway.
Re: Down vote?
Again, not obvious who/why downvoted, unless it is simply a True Believer jerking. The comment that I would baulk at is "If people are using pointer arithmetic, it's for a reason" as there is always a reason, but not always a good reason.
Sometimes it would be done to make code faster/simpler when a device has significant performance constraints, sometimes it is actually needed as you are writing an allocation library or similar, but sometimes just "because" and it looked cool at the time.
Now we can keep the C code.
Great! Now we can just keep that C code and use the Rust toolchain as a static checker. The Rust language itself can be hidden and never needs to hurt another programmer again!
Re: Now we can keep the C code.
Oh I like your way of thinking :)
More seriously though, code that was written to be multi-CPU and multi-OS usually has less problems as some of the fancy/stupid language, compiler, or library features are simply avoided for compatibility reasons.
Sounds like it could be useful if used for linting or converting to me
Sounds like it could be useful if used for C linting or gradual rust conversion over time to me. In some legacy C code you sometimes find pointers used where they don't have to be used. Of course all the code for hardware interrupts, DMA and critical regions would be pushed into C libraries and the more application like code converted to rust overtime. As long as the the binary assets are no bigger or slower I can't see why not to use this when one wants to reuse older source code for a new product.
Not impressed
Maybe I've missed something, but I'm unaware of an alternative to pointers when you have time critical code with decision trees that not only have to be fast, but also need fairly consistent execution time.
I appreciate the effort for Mini-C.
But "safe" subsets of C exist for decades, like the MISRA C standard that tells you what (not) to use.
That makes Mini-C "yet another subset" in my opinion.
It is good to apply Mini-C to changes, but before the full code tree can be "recompiled", there is a huge effort ahead.
Writing this, I wonder to what extend the "memory safety" issue is resolved by applying Mini-C by itself to existing code, so without moving to Rust?
HACL* PACL* me some EverClear champagne or else, Parse
Well, F* my OCaml! Hard to beat such tasty French KaRaMeL (formerly known as KReMLin) to satisy one's modulo taste bud [1]theories ! But let hardware exercise those fat pointers I say, that's where [2](concurrent) bounds checking on your code's data access waistline [3]belongs , so it fits through doors, buses, and pants, treasure caches of data and instructions, all without bloat, discomfort, or overflow! Get me a [4]Low* FPGA CPU to F*-run mini-C on and I'm sold (out!)! Word ...
But also, bless inria for its F*, Coq, and [5]Rocq Hard Prover (and Tom for this inspirational TF*A)! ;)
[1] https://en.wikipedia.org/wiki/Satisfiability_modulo_theories
[2] https://en.wikipedia.org/wiki/Lisp_machine#Initial_development
[3] https://en.wikipedia.org/wiki/Bounds_checking#Hardware_bounds_checking
[4] https://arxiv.org/abs/1703.00053
[5] https://coq.inria.fr/
Re: HACL* PACL* me some EverClear champagne or else, Parse
Nurse, he's got loose again !
“Minimal adjustments”
They make it sound so easy. If it was, why couldn’t they automate those adjustments too?