
Rewrite Linux Kernel in Rust? - z3phyr
https://dominuscarnufex.github.io/cours/rs-kernel/en.html
======
kbenson
Without making any assertions as to the benefits or problems of integrating
Rust into the Linux kernel to replace components, I just want to point out
it's really cool that you can and that someone has gone through the trouble of
documenting how. That's just awesome.

Discussion about whether this is a good idea or not, and the problems of doing
so, can now commence in earnest, and without the pesky problem of it being
mostly theoretical.

~~~
Jach
Yeah it's always great to see concepts implemented and working. My favorite
similar project I would use as an argument to bring Nim into a legacy C
codebase is
[https://github.com/ckkashyap/nimxv6](https://github.com/ckkashyap/nimxv6)
which started off by taking the xv6 kernel and replacing uart.c with uart.nim.
Every step of the way you can test your translation was correct, and once you
have Nim in the build process you can start using it for new things too.

~~~
meditationapp
Do you get anything with him? I was under the impression dereference safety
wasn't a priority.

~~~
Jach
Nim or Rust might be appropriate depending on the project. To me the idea of
replacing C with either of them comes down to a value-sell of how much you
value Rust's safety guarantees and whether you're willing to make the
investment of learning to use it or if you're just going to wrap everything in
unsafe{}. Considering you're talking to C programmers, I don't think they
value that safety very much. If they did, at least compared to other values,
they'd have moved to a GC language like Java long ago, or at least a
C++-subset-with-rules. So Nim has a lot of nice features that they might like
and expect from modern higher level languages, a lot you can use even if you
alloc() everywhere and avoid the GC, which you can do because it has an
optional and implementation-swappable GC. And it's not like Nim is just as
unsafe as C if you don't use the GC, the compiler stops you from shooting
yourself in the foot for a lot of things plus there are some good (tunable)
defaults on runtime behavior. Finally the ramp-up time to get productive in
Nim, and ongoing development effort, is probably less than Rust, which will
help the sell too.

~~~
devdoomari
pardon my ignorance, but wasn't "safe-and-performant" impossible before rust?

Isn't choosing a GC-language some trading off performance for safety?

~~~
Jach
It's a common misconception that GC languages must be slower than languages
without one. Where that misconception probably comes from is that it's pretty
easy to write performant code in C, and hard to write terribly slow code,
whereas in languages with GC in some instances you can do as good or better
but it's often harder. These days if you're comparing Java and C++, it's not
unusual for the Java code to be faster. The bigger tradeoff for GC is more in
the loss of determinism, some applications (like games) can't tolerate a
random stop-the-world collection for n ms. This can be mitigated by
pauseless/stoppable/real-time-guarantees GC implementations though so if your
language lets you swap the GC you have options around this. (For something
like a game, naive malloc/free aren't enough either. You're going to have to
manage your memory intelligently regardless of if you have a GC or not.)

As for Nim all I can say is run some benchmarks on C, Rust, and Nim. Compare
the ease of implementation. Compare the relative safeties beyond memory
ownership safety. Nim does very well.

~~~
jholman
You mentioned the determinism tradeoff, which is one the gamedev community
talks about endlessly. I'm no low-level expert, but my understanding is that
determinism isn't all you lose.

My understanding is that the big tradeoff is against RAM usage. You can have
relatively-low-RAM-usage JVM programs, and you can have fast JVM programs, but
you usually cannot have both. Keeping the total cost of GC low is done by
amortizing the cost over more run-time, which means running less GC often,
which means accumulating more garbage between runs. I mean, compared to the
insanity that is an Electron app or something, it's no big deal, but this
means that the pre-Rust tradeoff is something like "Memory safety, small
memory footprint, fast running time: choose two". (I want to emphasize that
I'm repeating hearsay, not reporting from my own knowledge.)

Also, it's probably a minor point, but JVM startup time is terrible compared
to a native binary. Many many programs don't care about this, but some do.

Code size (not counting the JVM) might well be improved, but if this is the
only JVM code on this machine and you need to pay that cost, that's a tradeoff
too.

~~~
Jach
You're right that total memory can be a tradeoff since any GC will necessarily
have some space overhead, the question is how much, and whether you can accept
some time tradeoff in exchange for less space. So again it all really depends
on the GC implementation (or implementations, e.g. standard Java can swap
implementations too). Remember that Java was originally designed to run on
embedded systems, Lisp has been used on very tiny hardware, and GC theory has
been developing from at least the 60s. Memory is plentiful compared to those
days.

My go-to reference when getting into GCs is
[http://gchandbook.org/](http://gchandbook.org/) and it covers all this in the
beginning. One old study tried a particular GC algorithm with Java and some
various program benchmarks and found to match perfect manual allocation time
(which will be better than in practice) you'll need about 5x the minimum
memory, 3x gives you 17% overhead. I think anywhere from 1.5x to 3x is normal
in practice since it depends on the application itself. There are tradeoffs
everywhere and it's great Rust offers another one.

I thought JVM startup time was a dead horse. :) Sure native beats it, so does
Python, but not by much these days.

------
martamoreno
It's a fun idea and many people have had it first thing when they heard of
Rust... So why did no one do it?

Quite simply: No one is going to rewrite the Linux Kernel in Rust. It is far
too big and also you are not solving any real issues either. Rust only
protects you from a small fraction of errors and while for an application like
a browser, this can be a big gain, I would argue that it is negligible for a
kernel in general. Reasons being that all the device IO, component
interaction, privilege escalations, logical errors, hardware errors, firmware
errors/bugs all can NOT be addressed by rust. Even for a browser, Rust is only
a band-aid. The amount of logical errors and security holes in something as
complex as a modern web-browser is more than enough of an attack surface. No
need for a rouge pointer to weird memory.

What is MUCH more viable though is a project to compartmentalize the Linux
Kernel into HVMs. I forgot the name but there are efforts to put nearly
everything into its own HVM. Which means if the printer driver goes nuts, it
can't really do anything to your system except not print anymore. If your
graphics driver goes nuts, well then you won't see anything... And so on.

This means, almost no code rewrites and still MUCH higher protection than
RUST. Rust does not compartmentalize. If any of your system components is
fucked, your whole system is still fucked. That is why it's pointless to
rewrite a kernel because of a language. You need to compartmentalize it...

Look at QubesOS for an early user-space effort. Would be nice to have a Qubes-
Kernel too.

~~~
i336_
You're referring to microkernel/nanokernel architectures.

Linus famously shunned Andrew Tanenbaum's MINIX kernel design and argued in
favor of a monolithic kernel, where buggy printer drivers live in the same
memory space and have the same elevated privileges as the code that manages
the kernel's secure crypto key ring.

Linus is also noted in this thread as not being interested in security issues.

I do agree with you about Rust not solving the hardware problem.

~~~
martamoreno
Yes it also makes sense. Because no matter what you do, it is unlikely that
your are able to compartmentalize something so critical and raw as a device
driver. If that one is fucked, the reason is likely that your system is
already compromised. It was just an example of what WOULD be more viable and
effective than rewriting it in Rust.

I still think that QubesOS is taking the right approach. Initially assume
hardware & kernel as trusted and make sure that this trust then can not be
violated from the outside (TPM, SecureBoot, VMs for each app, etc.). I just
wish more people would focus on that promising approach.

~~~
nine_k
> _able to compartmentalize something so critical and raw as a device driver._

QNX did this quite successfully; you could kill and reload e.g. a buggy
network or disk driver. All that on top of being a realtime OS.

------
jlrubin
This was a fun write up.

On the off chance the author is reading the comments here, there are a couple
security features they need to add for it to be more complete (I'm probably
missing some things).

1) They should add a call to access_ok(VERIFY_WRITE, pointer,
mem::size_of<Syscall>()) in rust_syscall_handle to ensures that the user space
pointer points to valid userspace memory of the right shape for the syscall
type.

2) They should sanitize (hopefully, using some zero-cost method) the Syscall
type to guarantee that it is well-formed before calling handle. If the
userspace constructs some normally impossible to construct Syscall, it's
unclear how a rust match pattern handles it (i.e., if an enum has only 3 types
and you pass in a falsely constructed variant with tag 100 is match guaranteed
not to just be using a jump table and accidentally jump 100 instructions?)

~~~
masklinn
Don't know about HN but a friend of the author posted this on reddit and was
commenting there:
[https://www.reddit.com/r/programming/comments/6f235k/rewrite...](https://www.reddit.com/r/programming/comments/6f235k/rewrite_the_linux_kernel_in_rust/dif9prj/)

edit: they also submitted it on HN, but it looks like they didn't get the
"winning" submission:
[https://news.ycombinator.com/submitted?id=fjallidergodur](https://news.ycombinator.com/submitted?id=fjallidergodur)

~~~
fjallidergodur
I am said friend, we're reading the discussion.

------
im_down_w_otp
Pretty cool. I decided to take a similar path in trying to improve/ensafen
parts of the BEAM VM by finding small chunks I could rewrite in Rust from C.

Most of the headache was build toolchain integration stuff. I did manage to
get a few simple things slotted in that appear to work transparently, which
made me hopeful for future more complicated things to play with like new
process mailbox implementations, etc.

Anyway, great write up and a cool project!

~~~
saghm
OT: I really like the word "ensafen", and I'm totally going to borrow it for
future use

~~~
pavement
It's a perfectly cromulent word, and embiggens every sentence and phrase that
includes it.

~~~
jholman
Embiggens? I think the point of the word is to debigulate some sentences. I
_would_ say that it embetters phrases that include it; maybe that's what you
meant.

~~~
pavement
Double plus good point.

------
Animats
I'd rather see a rewrite of QNX in Rust, as open source. The QNX kernel isn't
very large, but it offers most of POSIX, so you can run programs on it. (L4 is
nice, but it does so little that people just use it as a hypervisor to run
Linux. This doesn't simplify the problem.)

If you rewrite the Linux kernel in Rust, one module at a time, you'll just end
up with C written in Rust syntax, with raw pointers all over the place.

~~~
i336_
QNX is also realtime.

In order to provide realtime guarantees that cover small latencies, you need
infrastructure that generates as few instructions as possible. (If you have
lots of instructions you can hit targets of a hundreds of microseconds since
you can just wait. Start inching toward nanoseconds, though, and the
scheduler's own execution will get in the way unless it's crazy compact.)

The reason I mention this is that QNX runs on a variety of embedded devices
([http://qnx.symmetry.com.au/](http://qnx.symmetry.com.au/) has an interesting
list, universal remotes particularly jumped out at me) which are traditionally
quite slow.

What's Rust like in terms of generating code that's _consistently_ compact?

~~~
mafribe

       What's Rust like in ...
    

Isn't that less a question of Rust (which due to its affine types, should be
usable for generating code with predictable performance) but of compilers? It
should be possible to write a Rust compiler that optimises for predictable
performance at the cost of speed.

~~~
i336_
That's a fair point.

I don't quite follow why speed needs to be sacrificed to get predictable
performance though.

And I don't know about _writing_ a (whole new?!) Rust compiler (!) - if the
current Rust compiler wouldn't be a good fit for this purpose, well, I
definitely don't have the resources to anything about that, considering the
unbelievable amount of attention to detail and wrangling that's gone into
getting rustc to where it is today.

~~~
mafribe
Everything you say is true, but a _very_ simple compiler with little/no
optimisations should be quite easy to write. Indeed it is often coursework in
undergraduate compilers courses (for source languages simpler than Rust). The
main difficult would be the type-checker, but you could reuse the existing
one, for it does not affect run-time performance.

------
wyldfire
It would cease to be Linux, but by all means -- make it so!

BTW Redox OS (written in rust) originally supported Linux syscalls which I
thought was a super cool feature.

Jorge Aparicio started steed, a libc implemented in rust for rust. It seems
like a pretty interesting approach.

~~~
fjallidergodur
Author said:

Thank you. Actually, I have a series of articles about a libc written in Rust
for Rust which I started writing before steed was published, and… isn’t
finished yet. :-/ But maybe, I will finish and publish it eventually…

------
polygot
This is an interesting idea, but probably won't work out because Linus (and
other developers) may not want to transition away from the C language (see
Linus's comments about not using C++.) I think it would be excellent to see
Rust being used, but the implementation of replacing C with Rust could be very
complicated, and Rust's benefits may not be directly visible when having to
port/rewrite a lot of the components.

~~~
delhanty
If there were a fork and the fork were to gain momentum it might be not be up
to Linus.

A project like this might attract new developers because it might be less
intimidating than joining the Linux kernel proper. There would also be the
attraction of learning Rust in a very real world context.

edit:s/in/it

~~~
nickpsecurity
This is a good argument if a massive amount of effort was piled into this with
lots of refactoring on top of it. Meanwhile, it doesn't have a snowball's
chance in hell of accomplishing anything.

~~~
delhanty
The key for such a project being successful would be to set expectations low
in defining what success was. Something like "a proof of concept of an
experiment to rewrite small amounts of the Linux kernel in Rust".

One would fork Linux on GitHub, name it something other than Linux, and just
have the existing branches track Linux.

Linus has stated that he isn't particularly interested in security bugs. Other
people take a different view. Eg. [1], [2]

Another incremental step to improved security in Linux after a Rust fork might
be to take the lessons learned to create a conservative set of Rust inspired
extensions to C without all the hoariness of C++.

[1]
[https://news.ycombinator.com/item?id=2539839](https://news.ycombinator.com/item?id=2539839)

[2]
[https://www.schneier.com/blog/archives/2015/11/linus_torvald...](https://www.schneier.com/blog/archives/2015/11/linus_torvalds_.html)

edit 1: removed apparently dead link to original source
[http://lkml.org/lkml/2008/7/15/296](http://lkml.org/lkml/2008/7/15/296)

edit 2: clarify that one wouldn't use the name Linux for the fork

~~~
nickpsecurity
"One would fork Linux on GitHub, name it something other than Linux, and just
have the existing branches track Linux"

That's already hard given how many commits go into Linux. One must always
remember it's not just some volunteer project: most of the commits are by paid
programmers working at companies. That's where the labor comes from. You'd
need a lot of volunteers to keep up with the paid programmers. Even Linux
itself couldn't achieve that. Hence, all the paid programmers working on it.

"might be to take the lessons learned to create a conservative set of Rust
inspired extensions to C without all the hoariness of C++."

Now you're thinking on the right track. That sort of thing has been done
multiple times at the compiler level where they automagically make C safe with
a performance hit. Several teams did it at CPU level. These projects have been
used on FreeBSD mostly but also Linux. The strongest one so far is CheriBSD on
CHERI processor.

Alternatively, for a safer C that you're manually using, what you describe has
already been designed and actually inspired Rust's safety model:

[https://en.wikipedia.org/wiki/Cyclone_(programming_language)](https://en.wikipedia.org/wiki/Cyclone_\(programming_language\))

It could be revived and improved if people wanted. It got nowhere with C
programmers, though, in the past.

~~~
delhanty
So we agree about something at least ;-)

Slight tangent: I started my programming career in 1989.

The product was Parasolid and the language was AGA - a C inspired language
that preceded C++.

I vaguely remember the following extensions:

* built in logging with Undo

* overloaded operators leading to vector and interval types that worked better than C++

* a proper module system

~~~
nickpsecurity
Interesting. Never heard of it. The main safe language from that time was Ada.
It's been updated regularly. The neat thing about it is how it was
systematically designed to mitigate errors in about every operation.

[http://www.adacore.com/knowledge/technical-papers/safe-
and-s...](http://www.adacore.com/knowledge/technical-papers/safe-and-secure-
software-an-invitation-to-ada-2012/)

There's also a variant called SPARK that can automatically prove the absence
of errors like buffer overflow in a way that would normally take a theorem
prover.

[https://en.wikipedia.org/wiki/SPARK_(programming_language)](https://en.wikipedia.org/wiki/SPARK_\(programming_language\))

~~~
delhanty
>Interesting. Never heard of it.

You wouldn't of. It was developed internally to write Parasolid. Probably they
still use it - several of the original devs are still there 30 years later.

~~~
nickpsecurity
IBM did that with PL/S. It was a system version of PL/I with no runtime and
ability to decide how a function was compiled via annotations in that
function. Kept getting updated but no public release. Here's a few more that
you might find interesting for general usage that were proprietary or started
internal:

[http://www.semdesigns.com/Products/Parlanse/index.html](http://www.semdesigns.com/Products/Parlanse/index.html)

[https://web.archive.org/web/20130303055647/http://www.founda...](https://web.archive.org/web/20130303055647/http://www.foundationdb.com:80/white-
papers/flow/)

[https://en.wikipedia.org/wiki/Pike_(programming_language)](https://en.wikipedia.org/wiki/Pike_\(programming_language\))

[https://en.wikipedia.org/wiki/Roxen_(web_server)](https://en.wikipedia.org/wiki/Roxen_\(web_server\))

Parlanse is used in Semantic Designs' powerful toolkit. Flow was used to make
a Spanner competitor, FoundationDB, that was good enough in some way for Apple
to buy & pull off the market. Pike was used to build Roxen server that had
significant advantages in its time period. A number of companies also used
LISP and REBOL for their advantages with at least one asking LispWorks to do a
real-time implementation for telecom or something. So, usually it pays to go
with a common, often-crappy language with its ecosystem. However, doing one's
own language with competitive edge does pay off at times. I think these days
one can just do a DSL in Racket or Haskell on top of main language to negate
benefits of _totally_ rolling own stuff. People got far with it, though.
Galois is on a roll with that in high-assurance systems with this as an
example:

[https://ivorylang.org/ivory-introduction.html](https://ivorylang.org/ivory-
introduction.html)

So, just some fun examples of homebrew for you in return. :)

------
amluto
> (*pointer).handle();

Eek! That is, indeed, unsafe.

But this shouldn't be messing with the entry asm at all. Just add your new
syscall to the syscall table, just like any other syscall.

------
jclulow
I wonder what makes AT&T assembly syntax "objectively crap".

~~~
colejohnson66
You need to explicitly specify what the parameter sizes and types are. Plus,
the parameters are backwards compared to Intel syntax:

    
    
      Intel: mov eax, 5
      AT&T: movl $5, %eax
    

In basically every C-like programming language, you assign 5 to eax with
`eax=5` not `5=eax`. I can also tell that I'm working with the `long` variant
of `mov` just by looking at the types involved.

Intel's syntax for memory access seems more natural. If I don't want to add
`ebx` to the address, I can leave it out; I can't in AT&T; I have to have this
seemingly random comma. Plus, what's with the format?

    
    
      Intel: mov eax, [ds:eax*4+16]
      AT&T: movl ds:16(,%eax,4)

~~~
ajross
Interestingly, in the language _you actually wrote that complaint in_ :

> you assign 5 to eax

AT&T speaks English, basically. We move A to B. Destination goes last.

Both seem sane enough. And in fact both get really balled up when you have
three and four-argument instructions.

~~~
colejohnson66
I'll grant that, but it's a programming language, so I kind of expect it to
follow the order assignment does when I write in C++, C#, etc.

~~~
ajross
AT&T assembly style significantly predates C++ (and C# by like two decades),
and co-evolved with C. And strictly this isn't an assignment, anyway.
Assignment is an abstract operation in a programming language. This is a
machine instruction[1] for a physical machine, which does exactly one thing.

[1] Pause while the peanut gallery points out that in fact there are like
ninety six different instructions in x86 called some variant of "MOV". Yeah,
yeah, we know.

~~~
i336_
_What._

When did `mov` get 96 different instructions?

~~~
chipaca
I think it's just a bit of hyperbole. I don't know enough of this to know
whether
[http://c9x.me/x86/html/file_module_x86_id_176.html](http://c9x.me/x86/html/file_module_x86_id_176.html)
is complete, but it gives you a sense of it all. (also
[https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf](https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf)
if you haven't seen it)

~~~
i336_
Huh, interesting.

Apparently there are 35 rows in the table at
[http://www.felixcloutier.com/x86/MOV.html](http://www.felixcloutier.com/x86/MOV.html)
(according to Chrome and `$0.childElementCount`). I only discovered
gcc.godbolt.org links to documentation via context menu just the other day!

Regarding the PDF... _ouch_. Wonder if any executable obfuscators have tried
that approach :P (and I also wonder what sort of overhead it has.)

------
youdontknowtho
I guess one of the things that always comes to mind when someone brings up
"Rust all the things" is that it assumes that anything that can be expressed
in C can be expressed in Rust _with the safety features enabled_. Rust
wouldn't have a mechanism to do unsafe code otherwise, right? I think it might
be more complicated than we might think initially.

I personally think that someone should probably spend some more time on GC
that's performant enough for systems code. My convictions haven't driven me to
do anything about it, however.

~~~
Retra
Safe rust clearly cannot express everything that can be expressed in C. It
cannot even express everything that is safe in general. But "the safety
features of Rust" includes drawing a barrier between the safe parts of your
code and the unsafe parts. There is significant value in that
compartmentalization. Moreover, you are assuming that things written unsafely
in C should be written identically in rust, when there very well could be more
idiomatic, modern ways to do the same thing without any unsafety at all.

~~~
youdontknowtho
There are modern ways to express something safely that is written unsafely in
C. What Rust evangelists seem to forget is that there have been ways to do
that for a long time.

The article actually has very little safe Rust in it. It makes me wonder what
we are gaining? If the idea is that introducing Rust means that later you can
go back and add type and memory safety...why not use C++ in a type and safe by
construction way? Why not use Safe D?

I'm personally just looking forward to the hype dying down. I'm tired of
hearing about it, and most of the time I just click "hide" on Rust articles
because I'm not interested and people are starting to sound pretty shrill.

~~~
Retra
Have you ever written anything in Rust?

~~~
youdontknowtho
Like I said, I'm tired of hearing about it and, no, I haven't.

------
FrozenVoid
>Rewrite Linux Kernel in Rust? Rust has no backward compatibility: C code
compiles fine after decades. It would be a waste of time to rewrite Linux in
Rust when the resulting code has a lifetime of few months until it won't
compile and start breaking everywhere.

~~~
vog
_> C code compiles fine after decades_

Early C code doesn't compile on modern systems at all.

Speaking of early C code, every vendor had their own version. C
standardization had to go a long way.

Even today, writing portable C code is hard. Need a portable 128-bit integer?
Good luck on Windows. Need a portable 32-bit integer? Better take a C
implementation that provides int32_t, because just using "int" bites you when
switching from 32-bit to 64-bit platforms.

Oh, and int32_t was introduced in C99, which is not exactly "decades" ago, but
admittedly that was 18 years ago. However, I wouldn't bet on portability of
any code older than that, unless the authors of that C code were extremely
careful and anticipatory. But then, with enough effort (and perhaps code
generation), you can create portable code in any language.

Rust skips all that mess, by having a clear experimental phase in the 0.x
versions. It became stable with a clear commitment to backwards compatibility
since 1.0, which was released in 2015.

~~~
tom_mellior
> Need a portable 32-bit integer? Better take a C implementation that provides
> int32_t, because just using "int" bites you when switching from 32-bit to
> 64-bit platforms.

Citation needed. Which compilers for which machines are you talking about? I
don't think any change the size of int. What does break is the assumption that
int is the same size as pointers, but that's because the sizes of _pointers_
change.

~~~
vog
Whoops. I meant 16-bit versus 32-bit integers, when switching from 16-bit to
32-bit systems. Sorry for the confusion. Fortunately, they didn't repeat that
mess for 32/64 bits, at least not for integers, but "decades old code" is
still affected.

~~~
tom_mellior
Ah, yes. I agree, that was a mess!

------
based2
-> [https://mirage.io/](https://mirage.io/)

------
throwaway-1209
Let's see what Linus thinks about this.

~~~
astrobe_
Probably the same as the author of curl. Something along the lines of "Yes,
sure, our project is open source so fork it and go ahead".

~~~
throwaway-1209
"And don't call it Linux because I own the trademark".

------
lacampbell
What is the rust communities obsession with suggesting (threatening?) rewrites
of battle tested C programs that have been around for decades?

Now a kernel for a _new_ OS, that'd be something.

~~~
dbaupp
The article covers both of your points:

 _> Rust is supposed to be an excellent systems programming language, having
the ability to go extremely close to the bare metal, while offering a thick
layer of security and expressiveness which C lacks of. And such an assumption
has to be trialled!_

 _> Which Philipp Oppermann has already done by beginning to write a real OS
in Rust and writing about it. But this is not a realistic trial: an OS
starting from scratch has almost zero chance to end up being finished, not to
mention being actually used on real machines._

That is, a more accurate title might be "Proof of concept of incrementally
rewriting the Linux kernel in Rust".

