Re: [PATCH v3 5/7] rust: devres: add DevresLt for ForLt-aware device resource access

"Gary Guo" <[email protected]>
Newsgroups dev.linux.lists.nova-gpu,dev.linux.lists.driver-core,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pwm,org.kernel.vger.rust-for-linux
Message-ID <[email protected]>
On Fri Jun 19, 2026 at 12:08 AM BST, Danilo Krummrich wrote:
> Devres<T> stores resources as T and returns &'a T from access(). For
> lifetime-parameterized types like Bar<'a, SIZE> that are transmuted to
> 'static for storage, this exposes the synthetic 'static lifetime to
> callers -- any method on the stored type that returns a reference with
> its lifetime parameter would yield a &'static reference, which is
> unsound.
>
> Add DevresLt<F: ForLt>, a thin wrapper around Devres<F::Of<'static>>
> that shortens the stored 'static lifetime to the caller's borrow
> lifetime in all access methods.
>
> DevresLt::new() is unsafe because the caller must guarantee that the
> data remains valid for the device's full bound scope; the internal
> transmute from F::Of<'a> to F::Of<'static> would otherwise allow
> use-after-free.
>
> Two access patterns are provided:
>
> - CovariantForLt types get direct-reference accessors (access,
>   try_access) that return shortened references via
>   CovariantForLt::cast_ref.
>
> - Plain ForLt types use closure-based accessors (access_with,
>   try_access_with) whose universally quantified lifetime prevents
>   callers from smuggling in concrete short-lived references.
>
> Signed-off-by: Danilo Krummrich <[email protected]>
> ---
>  rust/kernel/devres.rs | 100 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 100 insertions(+)
>
> diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
> index 11ce500e9b76..e11deff3e1be 100644
> --- a/rust/kernel/devres.rs
> +++ b/rust/kernel/devres.rs
> @@ -24,6 +24,8 @@
>          Arc, //
>      },
>      types::{
> +        CovariantForLt,
> +        ForLt,
>          ForeignOwnable,
>          Opaque, //
>      },
> @@ -365,6 +367,104 @@ fn drop(&mut self) {
>      }
>  }
>  
> +/// Guard returned by [`DevresLt::try_access`].
> +///
> +/// Dereferences to `F::Of<'a>`, shortening the lifetime of the stored data to the guard's borrow
> +/// lifetime.
> +pub struct DevresGuard<'a, F: CovariantForLt>(RevocableGuard<'a, F::Of<'static>>);
> +
> +impl<'a, F: CovariantForLt> core::ops::Deref for DevresGuard<'a, F> {
> +    type Target = F::Of<'a>;
> +
> +    fn deref(&self) -> &Self::Target {
> +        F::cast_ref(&*self.0)
> +    }
> +}
> +
> +/// Device-managed resource with [`ForLt`](trait@ForLt)-aware access.
> +///
> +/// `DevresLt` wraps [`Devres`] and shortens the stored `'static` lifetime to the caller's borrow
> +/// lifetime in all access methods.
> +///
> +/// Types that implement [`trait@CovariantForLt`] get direct-reference accessors ([`Self::access`],
> +/// [`Self::try_access`]). Plain [`ForLt`](trait@ForLt) types use closure-based accessors
> +/// ([`Self::access_with`], [`Self::try_access_with`]).
> +pub struct DevresLt<F: ForLt>(Devres<F::Of<'static>>)
> +where
> +    F::Of<'static>: Send;

This bound looks weird. I know this is to satisfy `Devres`'s bound, but I think
this should actually read:

    for<'a> F::Of<'a>: Send

Best,
Gary

> +
> +impl<F: ForLt> DevresLt<F>
> +where
> +    F::Of<'static>: Send,
> +{
> +    /// Creates a new [`DevresLt`] instance of the given `data`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// The data must remain valid for the device's full bound scope. [`DevresLt`] allows
> +    /// access until the device is unbound, which may outlast `'a`.
> +    pub unsafe fn new<'a, E>(
> +        dev: &'a Device<Bound>,
> +        data: impl PinInit<F::Of<'a>, E>,
> +    ) -> Result<Self>
> +    where
> +        Error: From<E>,
> +    {
> +        // SAFETY: The caller guarantees the data is valid for the device's full bound scope.
> +        // Lifetimes do not affect layout, so F::Of<'a> and F::Of<'static> have identical
> +        // representation; casting the slot pointer is sound.
> +        let data = unsafe {
> +            pin_init::pin_init_from_closure::<F::Of<'static>, E>(move |slot| {
> +                data.__pinned_init(slot.cast())
> +            })
> +        };
> +
> +        Ok(Self(Devres::new(dev, data)?))
> +    }
> +
> +    /// Return a reference of the [`Device`] this [`DevresLt`] instance has been created with.
> +    pub fn device(&self) -> &Device {
> +        self.0.device()
> +    }
> +
> +    /// Obtain `&F::Of<'_>`, bypassing the [`Revocable`], through a closure.
> +    ///
> +    /// This method works like [`DevresLt::access`](DevresLt::access) but accepts any
> +    /// [`trait@ForLt`] type, not just [`trait@CovariantForLt`].
> +    pub fn access_with<R, G>(&self, dev: &Device<Bound>, f: G) -> Result<R>
> +    where
> +        G: for<'a> FnOnce(&F::Of<'a>) -> R,
> +    {
> +        self.0.access(dev).map(f)
> +    }
> +
> +    /// [`DevresLt`] accessor for [`Revocable::try_access_with`].
> +    pub fn try_access_with<R, G>(&self, f: G) -> Option<R>
> +    where
> +        G: for<'a> FnOnce(&F::Of<'a>) -> R,
> +    {
> +        self.0.data().try_access_with(f)
> +    }
> +}
> +
> +impl<F: CovariantForLt> DevresLt<F>
> +where
> +    F::Of<'static>: Send,
> +{
> +    /// Obtain `&'a F::Of<'a>`, bypassing the [`Revocable`].
> +    ///
> +    /// This method works like [`Devres::access`], but shortens the returned reference's lifetime
> +    /// from `'static` to `'a` via [`CovariantForLt::cast_ref`].
> +    pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a F::Of<'a>> {
> +        self.0.access(dev).map(F::cast_ref)
> +    }
> +
> +    /// [`DevresLt`] accessor for [`Revocable::try_access`].
> +    pub fn try_access(&self) -> Option<DevresGuard<'_, F>> {
> +        self.0.data().try_access().map(DevresGuard)
> +    }
> +}
> +
>  /// Consume `data` and [`Drop::drop`] `data` once `dev` is unbound.
>  fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
>  where
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.