[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAFJgqgRdiQ29bWvwsu11yokZb4OFF7pYYUU=ES6CYv9847KgVg@mail.gmail.com>
Date: Mon, 24 Feb 2025 09:57:41 -0700
From: Ventura Jack <venturajack85@...il.com>
To: Benno Lossin <benno.lossin@...ton.me>
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 Mon, Feb 24, 2025 at 5:47 AM Benno Lossin <benno.lossin@...ton.me> wrote:
>
> On 24.02.25 13:21, Ventura Jack wrote:
> >
> > 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`.
I think I see what you mean. The specific Rust "const reference"
`&UnsafeCell<T>` sort of behaves like C `T*`. But you have to get a
Rust "mutable raw pointer" `*mut T` when working with it using
`UnsafeCell::get()`. And you have to be careful with lifetimes if you
do any casts or share it or certain other things. And to dereference a
Rust "mutable raw pointer", you must use unsafe Rust. And you have to
understand aliasing.
One example I tested against MIRI:
use std::cell::UnsafeCell;
fn main() {
let val: UnsafeCell<i32> = UnsafeCell::new(42);
let x: & UnsafeCell<i32> = &val;
let y: & UnsafeCell<i32> = &val;
unsafe {
// UB.
//let pz: & i32 = & *val.get();
// UB.
//let pz: &mut i32 = &mut *val.get();
// Okay.
//let pz: *const i32 = &raw const *val.get();
// Okay.
let pz: *mut i32 = &raw mut *val.get();
let px: *mut i32 = x.get();
let py: *mut i32 = y.get();
*px = 0;
*py += 42;
*px += 24;
println!("x, y, z: {}, {}, {}", *px, *py, *pz);
}
}
It makes sense that the Rust "raw pointers" `*const i32` and `*mut
i32` are fine here, and that taking Rust "references" `& i32` and
`&mut i32` causes UB, since Rust "references" have aliasing rules that
must be followed.
Best, VJ.
Powered by blists - more mailing lists