Rust and UB
Rust and UB
Posted Aug 15, 2024 10:57 UTC (Thu) by pbonzini (subscriber, #60935)In reply to: Rust and UB by ralfj
Parent article: Standards for use of unsafe Rust in the kernel
> roughly speaking the way they can be implemented without having UB (even in theory) is to ensure that "your side" always uses atomic accesses.
I disagree that this is true, neither in practice nor in theory. Within a single process it is UB on the read side if the other side does a non-atomic write, and that's for good reasons, because for example the write could be torn. Torn writes of a pointer for example can lead to invalid memory accesses, which is quite clearly UB(*), and I don't see why it would be different across processes.
What you can expect in practice, is that the behavior will not be worse than dealing with garbage that was stored with atomic writes; if you validate your inputs you'll be fine. But I'm not sure that's true in theory, at least as things stand now.
(*) converting the AtomicUsize to a reference is unsafe, so Rust does maintain the promise that all UB can only happen within unsafe regions.
> (For Linux specifically, things are more complicated because Rust uses the C++ memory model but Linux implements its own concurrency model, the LKMM. That one *is* technically UB in Rust, just like it is technically UB in C. But this has nothing to do with having synchronization span kernel and userspace.)
In QEMU (written in C though Rust is coming), we use Linux-like wrappers for the C memory model. It turns out to be usable and just as optimized as the Linux primitives. So it would be possible to write Rust code using the Rust memory model, and to make the Rust memory model friendly enough to Linux kernel programmers.
However, we do have a couple tricks under our sleeve, where we "know" that some reorderings cannot happen at the processor level, and therefore use compiler fences instead of thread fences. For example instead, of the ill-fated Consume load, we use a Relaxed load followed by an Acquire compiler barrier. And before/after any RMW atomic, on x86 we use SeqCst compiler fences instead of a thread fence, not unlike smp_mb__before_atomic() in Linux. Those probably would *also* be technically UB...
I have long planned to write an article on the C/C++/Rust memory model for Linux kernel programmers, but never made the plan concrete. :(
