Re: C aggregate passing (Rust kernel policy)

Kent Overstreet
2025-02-25T20:55:12Z

On Tue, Feb 25, 2025 at 12:25:13PM -0800, Linus Torvalds wrote:
> On Tue, 25 Feb 2025 at 11:48, Kent Overstreet <kent.overstreet@linux.dev> wrote:
> >
> > Well, the whole point of unsafe is for the parts where the compiler
> > can't in general check for UB, so there's no avoiding that.
>
> No, that's most definitely NOT the whole point of unsafe.
>
> The point of unsafe is to bypass some rules, and write *SOURCE CODE*
> that does intentionally questionable things.

"Intentionally questionable"?

No, no, no.

That's not a term that has any meaning here; code is either correct or
it's not. We use unsafe when we need to do things that can't be
expressed in the model the compiler checks against - i.e. the model
where we can prove for all inputs that UB is impossible.

That does _not_ mean that there is no specification for what is and
isn't allowed: it just means that there is no way to check for all
inputs, _at compile time_, whether code obeys the spec.

> So if you are implementing the equivalent of malloc/free in unsafe
> rust, you want to be able to do things like arbitrary pointer
> arithmetic, because you are going to do very special things with the
> heap layout, like hiding your allocation metadata based on the
> allocation pointer, and then you want to do all the very crazy random
> arithmetic on pointers that very much do *not* make sense in safe
> code.

Yes, and the borrow checker has to go out the window.

> So unsafe rust is supposed to let the source code bypass those normal
> "this is what you can do to a pointer" rules, and create random new
> pointers that you then access.
>
> But when you then access those pointers, unsafe Rust should *NOT* say
> "oh, I'm now going to change the order of your accesses, because I
> have decided - based on rules that have nothing to do with your source
> code, and because you told me to go unsafe - that your unsafe pointer
> A cannot alias with your unsafe pointer B".

Well, not without sane rules everyone can follow, which _we never had in
C_.

In C, there's simply no model for derived pointers - this is why e.g.
restrict is just laughable. Because it's never just one pointer that
doesn't alias, we're always doing pointer arithmetic and computing new
pointers, so you need to be able to talk about _which_ pointers can't
alias.

This is the work we've been talking about with stacked/tree borrows: now
we do have that model. We can do pointer arithmetic, compute a new
pointer from a previous pointer (e.g. to get to the malloc header), and
yes of _course_ that aliases with the previous pointer - and the
compiler can understand that, and there are rules (that compiler can
even check, I believe) for "I'm doing writes through mutable derived
pointer A', I can't do any through A while A' exist".

See?

The problem isn't that "pointer aliasing is fundamentally unsafe and
dangerous and therefore the compiler just has to stay away from it
completely" - the problem has just been the lack of a workable model.

Much like how we went from "multithreaded programming is crazy and
dangerous", to "memory barriers are something you're just expected to
know how to use correctly".