lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <a4b79751-f1c8-4476-98a5-c59fb2e545ad@proton.me>
Date: Mon, 24 Feb 2025 12:47:32 +0000
From: Benno Lossin <benno.lossin@...ton.me>
To: Ventura Jack <venturajack85@...il.com>
Cc: Gary Guo <gary@...yguo.net>, Linus Torvalds <torvalds@...ux-foundation.org>, Kent Overstreet <kent.overstreet@...ux.dev>, airlied@...il.com, boqun.feng@...il.com, david.laight.linux@...il.com, ej@...i.de, gregkh@...uxfoundation.org, hch@...radead.org, hpa@...or.com, ksummit@...ts.linux.dev, linux-kernel@...r.kernel.org, miguel.ojeda.sandonis@...il.com, rust-for-linux@...r.kernel.org
Subject: Re: C aggregate passing (Rust kernel policy)

On 24.02.25 13:21, Ventura Jack wrote:
> On Mon, Feb 24, 2025 at 3:31 AM Benno Lossin <benno.lossin@...ton.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.

This is true for `&UnsafeCell<T>`. You can have multiple of those and
mutate the same value via only shared references. Note that
`UnsafeCell<T>` is `!Sync`, so it cannot be shared across threads, so
all of those shared references have to be on the same thread. (there is
the `SyncUnsafeCell<T>` type that is `Sync`, so it does allow for
across-thread mutations, but that is much more of a footgun, since you
still have to synchronize the writes/reads)

> The documentation for `UnsafeCell` conversely spends a lot of space
> discussing invariants and aliasing requirements.

Yes, since normally in Rust, you can either have exactly one mutable
reference, or several shared references (which cannot be used to mutate
a value). `UnsafeCell<T>` is essentially a low-level primitive that can
only be used with `unsafe` to build for example a mutex.

> 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.

Not sure how you arrived at that conclusion, the following code is legal
and sound Rust:

    let val = UnsafeCell::new(42);
    let x = &val;
    let y = &val;
    unsafe {
        *x.get() = 0;
        *y.get() = 42;
        *x.get() = 24;
    }

You can't do this with `&mut i32`.

> 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 .

Yes that is the description of all primitive pointer types. Both
references and raw pointers.

> 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?

Indeed.

>> 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`?

I am referring to the hack used by `Opaque`, it is `!Unpin` which
results in `&mut Opaque<T>` not having the `noalias` attribute.

>>> 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?

Yes, you can write `Opaque<T>` without using pinned-init. The hack
described above uses `PhantomPinned` to make `Opaque<T>: !Unpin`.

>>> "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.

Yes, that is something that I am working on at the moment.

> 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.

Yes, Rust for Linux uses `#![no_std]` (and also has its own alloc), so
it an use the stable version of pinned-init. However, there are several
differences between the current in-tree version and the user-space
version. I am working on some patches that fix that.

>>> 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.

Ah I see.

> 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?

Yes I agree.

---
Cheers,
Benno

>>> `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.


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ