Rewrite Linux Kernel in Rust?
dominuscarnufex.github.io
dominuscarnufex.github.io
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.
Isn't choosing a GC-language some trading off performance for safety?
C++11 already gets pretty close to that, but Microsoft's M# was used to write a research OS and is probably the closest.
Don't get me wrong, C++ is coming on by leaps and bounds, but if you want to get to safe and performant without GC, it's not the place you should be starting. (Equally, I'm not saying Rust solves all the world's problems. Just that it's a better place to start.)
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.
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.
My go-to reference when getting into GCs is 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.
Perhaps Nim can be used to write kernel modules already.
I agree. It's easy to criticize ideas that may seem futile at first, but there is nothing like a working implementation to take the wind out of the critics' proverbial sail.
It's a very detailed guide of how key components were incrementally improved in some way with actual source. That's almost always a good thing since it ranges from beneficial for intended goal to helping people learn things when it wasn't.
Thank you very much for your appreciation. In my opinion, such a toy projet would not be of much use if I didn’t document it so others can learn from it or improve on 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.
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.
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.
Writing in Rust might make the iteration/development process itself a little easier, I'll give it that.
I also like the idea about creating a system that does its best to isolate components and create a trustworthy environment. I'd probably pull the enforcement into the kernel though - not quite SELinux/grsecurity/AppArmor, more a system that completely isolates everything.
Kind of like the direction Linux is going with containerization, but developed that way from the start.
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.
I think you are right about the Qubes approach. Microsoft has started adopting that model in Windows. They started by moving parts of LSASS to a compartmentalized trusted code base in a VM. (Credential Guard). I think the next thing is actually running Edge with some hypervisor protections. Can't remember the feature name, though.
But that kind of thing is the future. You have to assume that things will be breached and deal with that upfront.
Please refrain from using strawman arguments. Nobody proposed to rewrite everything at once. This is what TFA actually wrote:
| So the idea would rather be to rewrite pieces of the Linux kernel in Rust, so the change can be incremental and one doesn’t need to rewrite the whole OS
See also: https://news.ycombinator.com/item?id=14479559
(It puzzles me what this strawman was good for, given that the remaining arguments are independent from the "too big" argument anyway.)
Would something like Ada SPARK be more helpful here? And what critical parts of that is Rust missing?
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?)
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
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!
What happened to your project?
I was just reading your comments and found this one:
>Erlang: the general disinterest of Ericsson and the hapless stewardship of Erlang Solutions.
Is that related to the reason that BEAM is no longer part of your day job?
The reason I ask is that I am thinking of starting a new project with BEAM, maybe some Rust or C++ and wondering whether Elixir has taken over from Erlang due to the disinterest of Ericsson.
Erlang is by far my favorite language and environment to use, and for a whole slew of things I'd still grab it for production projects/products.
I moved into the world of autonomous vehicle runtime software, and tools closer to the metal with deterministic timing characteristics are a requirement.
From what you write, it sounds like in production that, if one is attracted by the BEAM, there is less risk in choosing Erlang than Elixir.
One thing that concerns me for production use is the combination of the Erlang license and the possibility of Ericsson "doing a Sun" by selling the technology to Oracle, which might lead to an Android like situation.
It's trivial to include Erlang in an Elixir project, it's slightly less simple to go the other way, but it's still doable. In either case I don't think you have much in the way of risk regarding Ericsson pulling a Sun. When I say Ericsson is "disinterested" I just mean they're not inclined to promote or progress the language with the kind of pace and/or hype required to keep a thriving and active Silicon Valley shaped community going.
It's a tool that works well at exactly what it was designed for. There's no driving force trying to move it further to take over the general purpose programming world like Java had or Go has.
Anyway, we're epically off topic :-) I think my email is in my HN profile, so happy to take this offline.
Sorry and thank you again - was very interested in your experience.
Yep, still use C and C++.
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.
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/ 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?
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.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.
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.
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…
Rust might prove to be a possible candidate because it can help with safety. This is a different rationale than what the argument for C++ was about, so we cannot say.
The bigger problem I see is how premature the Rust eco-system and understanding is. It needs to go quite a long way people people start believing in it and before the language is well understood by more than a niche group of people who learn it just out of interest.
So what? It got built.
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
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
[2] https://www.schneier.com/blog/archives/2015/11/linus_torvald...
edit 1: removed apparently dead link to original source http://lkml.org/lkml/2008/7/15/296
edit 2: clarify that one wouldn't use the name Linux for the fork
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)
It could be revived and improved if people wanted. It got nowhere with C programmers, though, in the past.
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
http://www.adacore.com/knowledge/technical-papers/safe-and-s...
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.
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.
http://www.semdesigns.com/Products/Parlanse/index.html
https://web.archive.org/web/20130303055647/http://www.founda...
https://en.wikipedia.org/wiki/Pike_(programming_language)
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
So, just some fun examples of homebrew for you in return. :)
I guess the market will decide.
Ransomware will explode in the next few years.
If a big enough exploit gets pinned on Linux then sentiment could change and there could be a well-funded hard fork.
edit: saw this quoted on Twitter today (but can't recall where):
“Only a crisis - actual or perceived - produces real change. When that crisis occurs, the actions that are taken depend on the ideas that are lying around. That, I believe, is our basic function: to develop alternatives to existing policies, to keep them alive and available until the politically impossible becomes the politically inevitable.”
Milton Freidman
http://www.goodreads.com/quotes/110844-only-a-crisis---actua...
That's only true if you believe that the only possible accomplishment is to become the #1 kernel in the world.
Meanwhile, it's a very interesting project and it's a clear proof of technical proficiency. There are not many people in the world who can claim that they've written a kernel, let alone one in a cutting-edge programming language like Rust. I would love to have something like that on CV. Meanwhile, criticizing other people's projects does nothing to advance your career.
I was responding to a comment about doing the whole Linux kernel, not the original project. The original project makes sense. You gave another, good reason for that.
"Meanwhile, criticizing other people's projects does nothing to advance your career."
Professors grading things, peer review of research, managers reviewing performance, consumer organizations reviewing products... there's a lot of reasons that statement is wrong. I try to critique with an alternative solution presented and citations for both statements where possible. In the Linux case, I've often recommended tech at hardware and compiler level that automatically makes C code safe some of which have been used on FreeBSD and Linux. Alternatively, running it as a VM on top of a secure microkernel with security-critical apps running outside the VM.
Quite possible, but for the new project to be successful, existing _users_ will have to see clear benefits. The only way I can see those "new developers, using Rust" make a better product than "current developers with sometimes decades of experience, using C" if those experienced developers currently create lots of ownership related bugs.
IMO, _any_ argument claiming the way forward is a (partial) rewrite in Rust should start with some estimate of the number of ownership related bugs in the current code base.
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.
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)> 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.
[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.
When did `mov` get 96 different instructions?
Apparently there are 35 rows in the table at 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.)
There is no special precedence that should be applied to C. Similarity to C only imparts benefits to those that are fimilar with C-like languages or variants that follow that structure. That ends up being a lot of people because of how popular C and variants have been, but that's doesn't inherently make it better.
It's sort of a pet peeve of mine when people point out differences from C as problems without any objective facts to back up whether it actually is more or less useful in some aspects. If we never stood up to that type of thinking, we'd all by much poorer in the programming language department.
Many kinds of S expressions result in similar code. That would mean Lisp derivatives.
If "operator" has any meaning in assembly language, it would have to refer to the opcode. Both variants have the opcode (mov/movl) first.
> AT&T is Verb Subject Object. Intel is alien Verb Object Subject.
Both "move 5 into EAX" and "move into EAX the value 5" are imperatives. Neither of them has a subject, or more precisely, the subject is omitted (https://en.wikipedia.org/wiki/Imperative_mood). In both of these, 5 is an object. "into EAX" is an adverbial phrase or something.
It's true that one sounds more alien than the other when forcibly translated to English. Then again, assembly language is not English, so that's neither here nor there.
mov eax = 5
and use a similar style for other things, like: add eax = ebx, ecx
This is very nice and clear. But it only really works for three-address assembly languages, i.e., where the target register may be different from the sources. In classical x86, the target of an add is identical to one of the operands. So you would need something like add eax += ebx
or add eax = eax, ebx
where you must duplicate a register. Presumably when these assembly languages were developed 40 years ago, this was perceived as too much syntax.Looks absolutely horrible to me, why not simply
eax = ebx + ecx
?My gripe with AT&T style (and it's called AT&T because K&R wrote their assembler that way) is that it uses sigils to mark registers (something I've never seen any other assembler do) and specifically broke with the manufacturer's mnemonics. Yes, I can see why they did it that way (a consistent format for assembly across multiple architectures) but I don't necessarily agree with it (Go's assembler has gone father by not even bothering with the register names the manufacturer uses).
Almost forgot---another annoying aspect of AT&T is the use of "$" to designate an immediate value. Every assembler I used (prior to my exposure to Unix) used "#" for immediate value, and "$" for hexadecimal. But noooo, AT&T had to use "$" for immediate, and "0x" to designate hexadecimal and thus, I hit a brick wall each time I read code in AT&T format.
[1] Also interesting to note---Intel is little endian, Motorola is bigendian.
And always wondered why the need for % in AT&T for registers. It seems like there would no ambiguity based on instruction.
If anything, at&t follows English: move literal 5 into register eax.
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.
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.
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.
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.
... and then hilarity ensues at runtime due to latent UB that happened to "work" in the C implementation the original author used.
Now a kernel for a new OS, that'd be something.
> 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".
https://lobste.rs/s/ldnlos/have_you_considered_rewriting_it_...
Skade even dropped a HOWTO in one comment on replicating much of their community results in other projects. There's plenty of data from team members to look at there where I'll let the rest of you make decisions about it on your own.