Problem with your idea, is that low level kernel will use a lot of unsafe Rust, which will lose lot of benefits.
Problem with your idea, is that low level kernel will use a lot of unsafe Rust, which will lose lot of benefits.
I've actually worked on a toy kernel in Rust (using the excellent tutorial at https://os.phil-opp.com/), and it turns out that, yes, you obviously need to use unsafe code to talk to the actual hardware. But in most cases, you can encapsulate the low-level hardware inside a safe API:
https://github.com/emk/toyos-rs/blob/fdc5fb8cc8152a63d1b6c85...
In this example, only I/O port creation is an unsafe API, because you need to specify a memory address to read and write. But once the port is created (pointed at an appropriate address!), it's perfectly safe to use.
So, yes, kernel-space Rust will use "unsafe" far more often than regular Rust code. But you can still make at least 80% of your code safe, and maybe much more. And the remaining "unsafe" APIs act as a useful warning to pay attention to what you're doing. Plus, Rust is a really nice language to write kernel code in, anyway.
"Given enough eyeballs, all bugs are shallow" - but it helps when the eyeballs are focussed! :-)
I'm not a professional RE so my experience is limited, but when I went looking for vulns that's how I went about it, and I think that's generally the case.
With rust there's significantly less guesswork. That parser doesn't use unsafe? OK, let's start elsewhere. That seemingly innocent code uses unsafe? Great, check that out.
You can grep for vulnerabilities, basically.
I'm probably missing something obvious. But isn't that true for most languages?
My experience is that huge amount of C code running on my computer had exactly zero issues like this today. It has been working perfectly fine.
I run GNOME which uses a paradigm of checking all inputs to a function on entry via g_return(_val)_if_fail(assertion_expression). It helps the programmer do the right thing when it comes to using APIs that are disallowing NULL input.
Two days ago, the code on my workstation hit one of those "assertions":
Sep 26 23:40:31 core transmission-gt[1084]: g_file_test: assertion 'filename != NULL' failed
No oops in the kernel recently.
Millions of lines of desktop and kernel code, and one failed assertion in two days for using NULL incorrectly in the API.
So unless your standard is absolute perfection, it works fine as is.
I don't think I've ever run into a null reference in the real world. I'm sure it happens, especially if people write "&*some_function_that_might_return_null()". But it shouldn't be a normal thing. There are lots of other issues with C++, but this has never been a major one in my experience.
They are!
And while they are not a normal thing, they are a thing and I've run in to them a handful of times in the real world, almost always the result of someone not checking for null before dereferencing a pointer.
Rust does not really have this issue.
Because I can assure you, unsafe blocks in enterprise Rust will be reviewed as much as C and C++ code currently are in most Big Corps™.
We do have such problems with native libraries killing Java and .NET processes, with unsafe being the FFI boundary.
std::vector<int> x;
x.push_back(4);
int& val = x.back();
x.push_back(5); // ahh, val may be garbage now.
or Foo f;
Foo& fRef = f;
Foo g = std::move(f);My point was similar C/C++ don't have a safe subset.
If the platform has an MMU, if you've properly set up your page table mappings, etc. We are talking about writing an OS here!
Not to mention all the UB around null pointers that can cause miscompilation. Well, not technically miscompilation, but stuff like https://news.ycombinator.com/item?id=15324414
If there were a list "falsehoods software engineers believe about memory safety" this should be there.
Theoretically it's just all the same, but in practice having reduced interfaces does wonders to reduce the complexity and being able to reason about what brings problems, and what doesn't.
unsafe blocks are just a compromise where the programmer only has to prove safety of small blocks of code which aren't easily expressed in the type system.
http://programatica.cs.pdx.edu/House/
https://llvm.org/pubs/2009-08-12-UsenixSecurity-SafeSVAOS.ht...
So, that's not really a limitation. Worst-case scenario is proving those primitives correct with external tools whose preconditions and invariants are just checked by memory-safe code calling it. The above work shows worst case might not happen, though.
With Rust, it's easier just to replace the C library! In particular, cross-compiling with a static musl-libc has been supported out of the box with for a while now. For pure Rust programs (or ones with small amounts of C handled by cargo), just write:
cargo build --target=x86_64-unknown-linux-musl
This will produce a 100% static binary that relies on nothing except the kernel.For programs which require external C libraries, it's a bit trickier, because you need a static version of those libraries built against musl-libc. For common libraries like OpenSSL and libpq, I have a Docker container that makes this easier: https://github.com/emk/rust-musl-builder