8 ms·
> Consider https://os.phil-opp.com/page-tables/ https://os.phil-opp.com/page-tables/ for example, it uses the type system effectively to eliminate some issues,
by cwzwarich 9y ago
> Consider https://os.phil-opp.com/page-tables/ https://os.phil-opp.com/page-tables/ for example, it uses the type system effectively to eliminate some issues, in the end, it only has two calls to unsafe.
However, those two calls to unsafe rely on an invariant maintained by the rest of the code that is not marked unsafe. Effectively, the entire module has to be treated as unsafe code by the programmer.
- steveklabnik 9y agoYes, absolutely. It's still a far cry from what many people assume, which is that the whole shebang must be.
- cwzwarich 9y agoFor kernel-level programming, I think Rust is basically the same as C or C++ in this regard. I haven't seen a kernel written in Rust where I am convinced there is a submodule that can be treated as if its correctness doesn't impact the safety of the entire kernel.
- Rusky 9y ago> a submodule that can be treated as if its correctness doesn't impact the safety of the entire kernel. You don't have to go all that way to be better than C or C++. Rust can give you a submodule A whose correctness doesn't impact the safety of any uses of submodule B.
- cwzwarich 9y agoIf the correctness of submodule A impacts the safety of the entire kernel, then it impacts the safety of the uses of any other submodule, because you can't really contain the consequences of the unsafe behavior.
- Rusky 9y ago> you can't really contain the consequences of the unsafe behavior. That's true, but the property I described can still help you track down the root cause and limit the number of places that interact with it.
- TheDong 9y agoI don't understand this argument at all. "Rust is not perfect, so it's basically the same as C" The fact that "american fuzzy lop" and "syzkaller" have gone through the kernel and found dozens of use-after-free's (see https://github.com/google/syzkaller/blob/4bd70f/docs/linux/found_bugs.md https://github.com/google/syzkaller/blob/4bd70f/docs/linux/f...), shows that even incredibly carefully programmed C messes up in totally rote ways. The basic cleanup code which is able to be exploited here would not be in an unsafe block if this were rust. In fact, because rust abstracts memory management out better, most of the cleanup code wouldn't exist at all. Rust also allows better abstractions. For example, Rc<T> in rust is much easier to use without screwing up than the manually handled reference counters littered throughout the kernel. It seems obvious to me that numerous deadlocks in the kernel could be avoided by not hand-rolling refcounting everywhere. You're right that there's still the potential with rust for some code to compromise the safety of the entire kernel. But with rust that's a subset of code, not practically every single line of the whole thing. It seems to me like memory corruption/use-after-free/etc only being able to happen in a small fraction of code is better than it being able to happen anywhere.
- zaarn 9y ago> Rc<T> Rc requires memory allocation. When you boot a kernel you don't have that. Until you set it up, you can't use anything that requires a heap, only stack values. The best solution I found is to use C code to allocate raw memory and then go through the elaborate process of setting up kernel address space, after which you setup a frame allocator, after which you can setup interrupts and finally use pagefaults to normally use kernel heap. But wait there is more! During a pagefault interrupt you can't use the kernel heap in a writeful manner that is not inplace. Since a page fault during a page fault is a double fault and thusly your only option is to crash the kernel. You can only read from the kernel heap, modify in place in the heap and write to pagetables. Same goes for some other interrupts. Which btw, violate a lot of ways Rust code likes to work. An interrupt is like a coroutine but you don't have a nice runtime that abstracts it for you, instead the IRQ just smashes into the active execution and let's hope you haven't held any locks to important kernel stuff at that moment because you will have to crack that lock open.
- pcwalton 9y agoWhy is kernel programming any different from user-level programming here? I tend to think that the driver and network level, which is where most problems in the real world tend to lie, is similar to userland in how often unsafe code gets used and the impact of unsafe code on the correctness of the kernel. In fact, the fact that you can write some drivers in userland on certain OS's--and they can crash without bringing down the whole OS--is strong evidence of this.
- zaarn 9y agox86 is unsafe. Period. A lot of devices are unsafe. Period. You can write some device drivers in userspace. But you can't handle page faults in userspace. Those need to be handled by the kernel and will be 100% unsafe because any mistake will lead to a certain and quick double or triple fault. Kernel Level Programming is very very different to normal user-level programming. There is no malloc. There is no segmentation. Nothing prevents you from dereferencing a null pointer. In fact, in kernel-land a null pointer is valid memory. You don't have any of the forgiving properties of Ring3 assembly code in kernel-land.
- rcxdude 9y agoWhat you are describing is <10% of, say, the linux kernel. The rest of it reads very much like user-land code (and dereferencing a null pointer will often earn you a nice warning message if not a panic).
- zaarn 9y agoThose are usually the important 10% of a kernel that build the foundation of everything else. Once you have VMM and Paging setup, preventing a null pointer deref is not hard (simply unpage 0x0)
- andrewflnr 9y agoNo, that's sort of the problem, isn't it? Safety actually does depend on the whole shebang, or rather, the correctness of all the code that supplies inputs to the "unsafe" block. I think safety has to be thought about at level of the unit of code that can enforce its own invariants, rather than the specific syntax that does the dirty work.
- steveklabnik 9y agoSafety depends on any module containing unsafe code upholding the invariants that are required. But once you've done that, it's safe. The question is, how much code is in this state, vs being purely safe? My experience and that of others shows that it's generally a smaller amount than many people not experienced with Rust assume. Basically, I find the "there's always unsafe code somewhere at the bottom and therefore it's the same as 100% unsafe" to be overly reductive.
- frankmcsherry 9y ago> Basically, I find the "there's always unsafe code somewhere at the bottom and therefore it's the same as 100% unsafe" to be overly reductive. I totally agree; it's not the same. However, I think there is disagreement about whether saying only "there's always unsafe code somewhere at the bottom" is better or worse than saying "it's 100% unsafe". I tend to write some sketchy unsafe code, and the only thing I can say with a straight face is "please don't use this if you care about safety", i.e. treat it as if "it's 100% unsafe". I'm slightly weirded out that Rust has a keyword that amounts to "I understand UB really well; tell others to trust me on it transitively". I'll be less weirded out when the UB story gets shaken out more, or if cargo gets an "audit-unsafe" option that shows you all unsafe blocks you transitively depend on (does that exist yet?). Still like Rust, but letting randoms write unsafe code is scary.
- steveklabnik 9y agoThere is some tooling but not a ton. Check out the Miri link elsewhere in the thred; eventually I expect us to have amazing tools here. It’s gonna take some time though.