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 for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <56d1cc0e-df60-4e2b-bfa9-b29ec48a76b6@gmail.com>
Date: Sun, 18 Jan 2026 09:28:35 +0100
From: Dirk Behme <dirk.behme@...il.com>
To: Boqun Feng <boqun.feng@...il.com>, rust-for-linux@...r.kernel.org,
 linux-kernel@...r.kernel.org, rcu@...r.kernel.org
Cc: Miguel Ojeda <ojeda@...nel.org>, Gary Guo <gary@...yguo.net>,
 Björn Roy Baron <bjorn3_gh@...tonmail.com>,
 Benno Lossin <lossin@...nel.org>, Andreas Hindborg <a.hindborg@...nel.org>,
 Alice Ryhl <aliceryhl@...gle.com>, Trevor Gross <tmgross@...ch.edu>,
 Danilo Krummrich <dakr@...nel.org>, Will Deacon <will@...nel.org>,
 Peter Zijlstra <peterz@...radead.org>, Mark Rutland <mark.rutland@....com>,
 "Paul E. McKenney" <paulmck@...nel.org>,
 Frederic Weisbecker <frederic@...nel.org>,
 Neeraj Upadhyay <neeraj.upadhyay@...nel.org>,
 Joel Fernandes <joelagnelf@...dia.com>, Josh Triplett
 <josh@...htriplett.org>, Uladzislau Rezki <urezki@...il.com>,
 Steven Rostedt <rostedt@...dmis.org>,
 Mathieu Desnoyers <mathieu.desnoyers@...icios.com>,
 Lai Jiangshan <jiangshanlai@...il.com>, Zqiang <qiang.zhang@...ux.dev>,
 FUJITA Tomonori <fujita.tomonori@...il.com>
Subject: Re: [PATCH 5/5] rust: sync: rcu: Add RCU protected pointer

On 17.01.26 13:22, Boqun Feng wrote:
> RCU protected pointers are an atomic pointer that can be loaded and
> dereferenced by mulitple RCU readers, but only one updater/writer can
> change the value (following a read-copy-update pattern usually).
> 
> This is useful in the case where data is read-mostly. The rationale of
> this patch is to provide a proof of concept on how RCU should be exposed
> to the Rust world, and it also serves as an example for atomic usage.
> 
> Similar mechanisms like ArcSwap [1] are already widely used.
> 
> Provide a `Rcu<P>` type with an atomic pointer implementation. `P` has
> to be a `ForeignOwnable`, which means the ownership of a object can be
> represented by a pointer-size value.
> 
> `Rcu::dereference()` requires a RCU Guard, which means dereferencing is
> only valid under RCU read lock protection.
> 
> `Rcu::copy_update()` is the operation for updaters, it requries a
> `Pin<&mut Self>` for exclusive accesses, since RCU updaters are normally
> exclusive with each other.
> 
> A lot of RCU functionalities including asynchronously free (call_rcu()
> and kfree_rcu()) are still missing, and will be the future work.
> 
> Also, we still need language changes like field projection [2] to
> provide better ergonomic.
> 
> Acknowledgment: this work is based on a lot of productive discussions
> and hard work from others, these are the ones I can remember (sorry if I
> forgot your contribution):
> 
> * Wedson started the work on RCU field projection and Benno followed it
>   up and had been working on it as a more general language feature.
>   Also, Gary's field-projection repo [3] has been used as an example for
>   related discussions.
> 
> * During Kangrejos 2023 [4], Gary, Benno and Alice provided a lot of
>   feedbacks on the talk from Paul and me: "If you want to use RCU in
>   Rust for Linux kernel..."
> 
> * During a recent discussion among Benno, Paul and me, Benno suggested
>   using `Pin<&mut>` to guarantee the exclusive access on updater
>   operations.
> 
> Link: https://crates.io/crates/arc-swap [1]
> Link: https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/Field.20Projections/near/474648059 [2]
> Link: https://github.com/nbdd0121/field-projection [3]
> Link: https://kangrejos.com/2023 [4]
> Signed-off-by: Boqun Feng <boqun.feng@...il.com>
> ---
>  rust/kernel/sync/rcu.rs | 326 +++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 325 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> index a32bef6e490b..28bbccaa2e5e 100644
> --- a/rust/kernel/sync/rcu.rs
> +++ b/rust/kernel/sync/rcu.rs
> @@ -4,7 +4,23 @@
>  //!
>  //! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
>  
> -use crate::{bindings, types::NotThreadSafe};
> +use crate::bindings;
> +use crate::{
> +    sync::atomic::{
> +        Atomic,
> +        Relaxed,
> +        Release, //
> +    },
> +    types::{
> +        ForeignOwnable,
> +        NotThreadSafe, //
> +    },
> +};
> +use core::{
> +    marker::PhantomData,
> +    pin::Pin,
> +    ptr::NonNull, //
> +};
>  
>  /// Evidence that the RCU read side lock is held on the current thread/CPU.
>  ///
> @@ -50,3 +66,311 @@ fn drop(&mut self) {
>  pub fn read_lock() -> Guard {
>      Guard::new()
>  }
> +
> +use crate::types::Opaque;
> +
> +/// A temporary `UnsafePinned` [1] that provides a way to opt-out typical alias rules for mutable
> +/// references.
> +///
> +/// # Invariants
> +///
> +/// `self.0` is always properly initialized.
> +///
> +/// [1]: https://doc.rust-lang.org/std/pin/struct.UnsafePinned.html
> +struct UnsafePinned<T>(Opaque<T>);
> +
> +impl<T> UnsafePinned<T> {
> +    const fn new(value: T) -> Self {
> +        // INVARIANTS: `value` is initialized.
> +        Self(Opaque::new(value))
> +    }
> +
> +    const fn get(&self) -> *mut T {
> +        self.0.get()
> +    }
> +}
> +
> +// SAFETY: `UnsafePinned` is safe to transfer between execution contexts as long as `T` is `Send`.
> +unsafe impl<T: Send> Send for UnsafePinned<T> {}
> +// SAFETY: `UnsafePinned` is safe to shared between execution contexts as long as `T` is `Sync`.
> +unsafe impl<T: Sync> Sync for UnsafePinned<T> {}
> +
> +/// An RCU protected pointer, the pointed object is protected by RCU.
> +///
> +/// # Invariants
> +///
> +/// Either the pointer is null, or it points to a return value of
> +/// [`ForeignOwnable::into_foreign()`] and the atomic variable exclusively owns the pointer.
> +pub struct Rcu<P: ForeignOwnable>(
> +    UnsafePinned<Atomic<*mut crate::ffi::c_void>>,
> +    PhantomData<P>,
> +);
> +
> +/// A pointer that has been unpublished, but hasn't waited for a grace period yet.
> +///
> +/// The pointed object may still have an existing RCU reader. Therefore a grace period is needed to
> +/// free the object.
> +///
> +/// # Invariants
> +///
> +/// The pointer has to be a return value of [`ForeignOwnable::into_foreign`] and [`Self`]
> +/// exclusively owns the pointer.
> +pub struct RcuOld<P: ForeignOwnable>(NonNull<crate::ffi::c_void>, PhantomData<P>);
> +
> +impl<P: ForeignOwnable> Drop for RcuOld<P> {
> +    fn drop(&mut self) {
> +        // SAFETY: As long as called in a sleepable context, which should be checked by klint,
> +        // `synchronize_rcu()` is safe to call.
> +        unsafe {
> +            bindings::synchronize_rcu();
> +        }
> +
> +        // SAFETY: `self.0` is a return value of `P::into_foreign()`, so it's safe to call
> +        // `from_foreign()` on it. Plus, the above `synchronize_rcu()` guarantees no existing
> +        // `ForeignOwnable::borrow()` anymore.
> +        let p: P = unsafe { P::from_foreign(self.0.as_ptr()) };
> +        drop(p);
> +    }
> +}
> +
> +impl<P: ForeignOwnable> Rcu<P> {
> +    /// Creates a new RCU pointer.
> +    pub fn new(p: P) -> Self {
> +        // INVARIANTS: The return value of `p.into_foreign()` is directly stored in the atomic
> +        // variable.
> +        Self(
> +            UnsafePinned::new(Atomic::new(p.into_foreign())),
> +            PhantomData,
> +        )
> +    }
> +
> +    fn as_atomic(&self) -> &Atomic<*mut crate::ffi::c_void> {
> +        // SAFETY: Per type invariants of `UnsafePinned`, `self.0.get()` points to an initialized
> +        // `&Atomic`.
> +        unsafe { &*self.0.get() }
> +    }
> +
> +    fn as_atomic_mut_pinned(self: Pin<&mut Self>) -> &Atomic<*mut crate::ffi::c_void> {
> +        self.into_ref().get_ref().as_atomic()
> +    }
> +
> +    /// Dereferences the protected object.
> +    ///
> +    /// Returns `Some(b)`, where `b` is a reference-like borrowed type, if the pointer is not null,
> +    /// otherwise returns `None`.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```rust
> +    /// # use kernel::alloc::{flags, KBox};
> +    /// use kernel::sync::rcu::{self, Rcu};
> +    ///
> +    /// let x = Rcu::new(KBox::new(100i32, flags::GFP_KERNEL)?);
> +    ///
> +    /// let g = rcu::read_lock();
> +    /// // Read in under RCU read lock protection.
> +    /// let v = x.dereference(&g);
> +    ///
> +    /// assert_eq!(v, Some(&100i32));
> +    ///
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    ///
> +    /// Note the borrowed access can outlive the reference of the [`Rcu<P>`], this is because as
> +    /// long as the RCU read lock is held, the pointed object should remain valid.
> +    ///
> +    /// In the following case, the main thread is responsible for the ownership of `shared`, i.e. it
> +    /// will drop it eventually, and a work item can temporarily access the `shared` via `cloned`,
> +    /// but the use of the dereferenced object doesn't depend on `cloned`'s existence.
> +    ///
> +    /// ```rust
> +    /// # use kernel::alloc::{flags, KBox};
> +    /// # use kernel::workqueue::system;
> +    /// # use kernel::sync::{Arc, atomic::{Atomic, Acquire, Release}};
> +    /// use kernel::sync::rcu::{self, Rcu};
> +    ///
> +    /// struct Config {
> +    ///     a: i32,
> +    ///     b: i32,
> +    ///     c: i32,
> +    /// }
> +    ///
> +    /// let config = KBox::new(Config { a: 1, b: 2, c: 3 }, flags::GFP_KERNEL)?;
> +    ///
> +    /// let shared = Arc::new(Rcu::new(config), flags::GFP_KERNEL)?;
> +    /// let cloned = shared.clone();
> +    ///
> +    /// // Use atomic to simulate a special refcounting.
> +    /// static FLAG: Atomic<i32> = Atomic::new(0);
> +    ///
> +    /// system().try_spawn(flags::GFP_KERNEL, move || {
> +    ///     let g = rcu::read_lock();
> +    ///     let v = cloned.dereference(&g).unwrap();
> +    ///     drop(cloned); // release reference to `shared`.
> +    ///     FLAG.store(1, Release);
> +    ///
> +    ///     // but still need to access `v`.
> +    ///     assert_eq!(v.a, 1);
> +    ///     drop(g);
> +    /// });
> +    ///
> +    /// // Wait until `cloned` dropped.
> +    /// while FLAG.load(Acquire) == 0 {
> +    ///     // SAFETY: Sleep should be safe.
> +    ///     unsafe { kernel::bindings::schedule(); }
> +    /// }
> +    ///
> +    /// drop(shared);
> +    ///
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn dereference<'rcu>(&self, _rcu_guard: &'rcu Guard) -> Option<P::Borrowed<'rcu>> {
> +        // Ordering: Address dependency pairs with the `store(Release)` in copy_update().
> +        let ptr = self.as_atomic().load(Relaxed);
> +
> +        if !ptr.is_null() {
> +            // SAFETY:


Would it be an option to take an early return here and with this drop
one indentation level of the larger `SAFETY` comment?

if ptr.is_null() {
   return None;
}

// SAFETY:
...
Some(unsafe { P::borrow(ptr) })

Same for `Drop for Rcu<P>` below.


> +            // - Since `ptr` is not null, so it has to be a return value of `P::into_foreign()`.
> +            // - The returned `Borrowed<'rcu>` cannot outlive the RCU Guar, this guarantees the

Guar -> Guard

> +            //   return value will only be used under RCU read lock, and the RCU read lock prevents
> +            //   the pass of a grace period that the drop of `RcuOld` or `Rcu` is waiting for,
> +            //   therefore no `from_foreign()` will be called for `ptr` as long as `Borrowed` exists.
> +            //
> +            //      CPU 0                                       CPU 1
> +            //      =====                                       =====
> +            //      { `x` is a reference to Rcu<Box<i32>> }
> +            //      let g = rcu::read_lock();
> +            //
> +            //      if let Some(b) = x.dereference(&g) {
> +            //      // drop(g); cannot be done, since `b` is still alive.
> +            //
> +            //                                              if let Some(old) = x.replace(...) {
> +            //                                                  // `x` is null now.
> +            //          println!("{}", b);
> +            //      }
> +            //                                                  drop(old):
> +            //                                                    synchronize_rcu();
> +            //      drop(g);
> +            //                                                    // a grace period passed.
> +            //                                                    // No `Borrowed` exists now.
> +            //                                                    from_foreign(...);
> +            //                                              }
> +            Some(unsafe { P::borrow(ptr) })
> +        } else {
> +            None
> +        }
> +    }
> +
> +    /// Read, copy and update the pointer with new value.
> +    ///
> +    /// Returns `None` if the pointer's old value is null, otherwise returns `Some(old)`, where old
> +    /// is a [`RcuOld`] which can be used to free the old object eventually.
> +    ///
> +    /// The `Pin<&mut Self>` is needed because this function needs the exclusive access to
> +    /// [`Rcu<P>`], otherwise two `copy_update()`s may get the same old object and double free.
> +    /// Using `Pin<&mut Self>` provides the exclusive access that C side requires with the type
> +    /// system checking.
> +    ///
> +    /// Also this has to be `Pin` because a `&mut Self` may allow users to `swap()` safely, that
> +    /// will break the atomicity. A [`Rcu<P>`] should be structurally pinned in the struct that
> +    /// contains it.
> +    ///
> +    /// Note that `Pin<&mut Self>` cannot assume noalias on `self.0` here because of `self.0` is an
> +    /// [`UnsafePinned`].
> +    ///
> +    /// [`UnsafePinned`]: https://doc.rust-lang.org/std/pin/struct.UnsafePinned.html
> +    pub fn copy_update<F>(self: Pin<&mut Self>, f: F) -> Option<RcuOld<P>>
> +    where
> +        F: FnOnce(Option<P::Borrowed<'_>>) -> Option<P>,
> +    {
> +        let inner = self.as_atomic_mut_pinned();
> +
> +        // step 1: COPY, or more generally, initializing `new` based on `old`.
> +        // Ordering: Address dependency pairs with the `store(Release)` in copy_update().
> +        let old_ptr = NonNull::new(inner.load(Relaxed));
> +
> +        let old = old_ptr.map(|nonnull| {
> +            // SAFETY: Per type invariants `old_ptr` has to be a value return by a previous
> +            // `into_foreign()`, and the exclusive reference `self` guarantees that `from_foreign()`
> +            // has not been called.
> +            unsafe { P::borrow(nonnull.as_ptr()) }
> +        });
> +
> +        let new = f(old);
> +
> +        // step 2: UPDATE.
> +        if let Some(new) = new {
> +            let new_ptr = new.into_foreign();
> +            // Ordering: Pairs with the address dependency in `dereference()` and
> +            // `copy_update()`.
> +            // INVARIANTS: `new.into_foreign()` is directly store into the atomic variable.
> +            inner.store(new_ptr, Release);
> +        } else {
> +            // Ordering: Setting to a null pointer doesn't need to be Release.
> +            // INVARIANTS: The atomic variable is set to be null.
> +            inner.store(core::ptr::null_mut(), Relaxed);
> +        }
> +
> +        // INVARIANTS: The exclusive reference guarantess that the ownership of a previous
> +        // `into_foreign()` transferred to the `RcuOld`.
> +        Some(RcuOld(old_ptr?, PhantomData))
> +    }
> +
> +    /// Replaces the pointer with new value.
> +    ///
> +    /// Returns `None` if the pointer's old value is null, otherwise returns `Some(old)`, where old
> +    /// is a [`RcuOld`] which can be used to free the old object eventually.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```rust
> +    /// use core::pin::pin;
> +    /// # use kernel::alloc::{flags, KBox};
> +    /// use kernel::sync::rcu::{self, Rcu};
> +    ///
> +    /// let mut x = pin!(Rcu::new(KBox::new(100i32, flags::GFP_KERNEL)?));
> +    /// let q = KBox::new(101i32, flags::GFP_KERNEL)?;
> +    ///
> +    /// // Read in under RCU read lock protection.
> +    /// let g = rcu::read_lock();
> +    /// let v = x.dereference(&g);
> +    ///
> +    /// // Replace with a new object.
> +    /// let old = x.as_mut().replace(q);
> +    ///
> +    /// assert!(old.is_some());
> +    ///
> +    /// // `v` should still read the old value.
> +    /// assert_eq!(v, Some(&100i32));
> +    ///
> +    /// // New readers should get the new value.
> +    /// assert_eq!(x.dereference(&g), Some(&101i32));
> +    ///
> +    /// drop(g);
> +    ///
> +    /// // Can free the object outside the read-side critical section.
> +    /// drop(old);
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn replace(self: Pin<&mut Self>, new: P) -> Option<RcuOld<P>> {
> +        self.copy_update(|_| Some(new))
> +    }
> +}
> +
> +impl<P: ForeignOwnable> Drop for Rcu<P> {
> +    fn drop(&mut self) {
> +        let ptr = self.as_atomic().load(Relaxed);
> +        if !ptr.is_null() {
> +            // SAFETY: As long as called in a sleepable context, which should be checked by klint,
> +            // `synchronize_rcu()` is safe to call.
> +            unsafe {
> +                bindings::synchronize_rcu();
> +            }
> +
> +            // SAFETY: `self.0` is a return value of `P::into_foreign()`, so it's safe to call
> +            // `from_foreign()` on it. Plus, the above `synchronize_rcu()` guarantees no existing
> +            // `ForeignOwnable::borrow()` anymore.
> +            drop(unsafe { P::from_foreign(ptr) });
> +        }
> +    }
> +}


Best regards

Dirk

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ