Re: C aggregate passing (Rust kernel policy)

Ventura Jack
2025-02-24T12:21:37Z

On Mon, Feb 24, 2025 at 3:31 AM Benno Lossin <benno.lossin@proton.me> wrote:
>
> On 24.02.25 10:57, Ventura Jack wrote:
> >
> > In regards to `UnsafeCell`, I believe that you are correct in regards
> > to mutability. However, if I understand you correctly, and if I
> > am not mistaken, I believe that you are wrong about `UnsafeCell`
> > making it possible to opt-out of the aliasing rules. And thus that
> > `UnsafeCell` does not behave like `T*` in C.
>
> `UnsafeCell<T>` does not behave like `T*` in C, because it isn't a
> pointer. Like Gary said, `&UnsafeCell<T>` behaves like `T*` in C, while
> `&mut UnsafeCell<T>` does not. That is what you quote from the docs
> below. (Those ampersands mark references in Rust, pointers that have
> additional guarantees [1])

From what I can see in the documentation, `&UnsafeCell<T>` also does not
behave like `T*` in C. In C, especially if "strict aliasing" is turned
off in the
compiler, `T*` does not have aliasing requirements. You can have multiple
C `T*` pointers pointing to the same object, and mutate the same object.
The documentation for `UnsafeCell` conversely spends a lot of space
discussing invariants and aliasing requirements.

I do not understand why you claim:

"`&UnsafeCell<T>` behaves like `T*` in C,"

That statement is false as far as I can figure out, though I have taken it
out of context here. Is the argument in regards to mutability? But `T*` in C
allows mutability. If you looked at C++ instead of C, maybe a `const`
pointer would be closer in semantics and behavior.

> below. (Those ampersands mark references in Rust, pointers that have
> additional guarantees [1])
>
>[omitted]
>
> [1]: https://doc.rust-lang.org/std/primitive.reference.html

There is also https://doc.rust-lang.org/reference/types/pointer.html .
But, references must follow certain aliasing rules, and in unsafe Rust,
it is the programmer that has the burden of upholding those aliasing rules,
right?

> For disabling the uniqueness guarantee for `&mut`, we use an official
> "hack" that the Rust language developers are working on replacing with
> a better mechanism (this was also mentioned by Gary above).

Are you referring to `Opaque`?

> > Documentation and related links for `Opaque`:
> > https://rust.docs.kernel.org/kernel/types/struct.Opaque.html
> > https://rust.docs.kernel.org/src/kernel/types.rs.html#307-310
> > https://github.com/Rust-for-Linux/pinned-init
> >
> > It uses `UnsafeCell`, Rust "pinning", and the Rust for Linux library
> > "pinned-init".
>
> pinned-init is not specific to `Opaque` and not really relevant with
> respect to discussing aliasing guarantees.

Is `Opaque` really able to avoid aliasing requirements for users,
without internally using "pinned-init"/derivative or the pinning
feature used in its implementation?

> > "pinned-init" uses a number of experimental, unstable and nightly
> > features of Rust.
>
> This is wrong. It uses no unstable features when you look at the version
> in-tree (at `rust/kernel/init.rs`). The user-space version uses a single
> unstable feature: `allocator_api` for accessing the `AllocError` type
> from the standard library. You can disable the `alloc` feature and use
> it on a stable compiler as written in the readme.

Interesting, I did not realize that the Rust for Linux project uses
a fork or derivative of "pinned-init" in-tree, not "pinned-init" itself.

What I can read in the README.md:
https://github.com/Rust-for-Linux/pinned-init/tree/main

"Nightly Needed for alloc feature

This library requires the allocator_api unstable feature
when the alloc feature is enabled and thus this feature
can only be used with a nightly compiler. When enabling
the alloc feature, the user will be required to activate
allocator_api as well.

The feature is enabled by default, thus by default
pinned-init will require a nightly compiler. However, using
the crate on stable compilers is possible by disabling alloc.
In practice this will require the std feature, because stable
compilers have neither Box nor Arc in no-std mode."

Rust in Linux uses no_std, right? So Rust in Linux would not be
able to use the original "pinned_init" library as it currently is without
using currently nightly/unstable features, until the relevant feature(s)
is stabilized.

> > Working with the library implementation requires having a good
> > understanding of unsafe Rust and many advanced features of Rust.
>
> pinned-init was explicitly designed such that you *don't* have to write
> unsafe code for initializing structures that require pinning from the
> get-go (such as the kernel's mutex).

Sorry, I sought to convey that I was referring to the internal library
implementation, not the usage of the library.

For the library implementation, do you agree that a good
understanding of unsafe Rust and many advanced features
are required to work with the library implementation? Such as
pinning?

> > `Opaque` looks interesting. Do you know if it will become a more
> > widely used abstraction outside the Linux kernel?
>
> Only in projects that do FFI with C/C++ (or other such languages).
> Outside of that the `Opaque` type is rather useless, since it disables
> normal guarantees and makes working with the inner type annoying.

Interesting.

Best, VJ.