mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
rust: devres: add DevresLt for ForLt-aware device resource access
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. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://patch.msgid.link/20260626183630.2585057-6-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
6763c8876d
commit
b85f672a3e
|
|
@ -24,6 +24,8 @@
|
|||
Arc, //
|
||||
},
|
||||
types::{
|
||||
CovariantForLt,
|
||||
ForLt,
|
||||
ForeignOwnable,
|
||||
Opaque, //
|
||||
},
|
||||
|
|
@ -365,6 +367,110 @@ 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>;
|
||||
|
||||
#[inline]
|
||||
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
|
||||
for<'a> F::Of<'a>: Send;
|
||||
|
||||
impl<F: ForLt> DevresLt<F>
|
||||
where
|
||||
for<'a> F::Of<'a>: 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.
|
||||
#[inline]
|
||||
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`].
|
||||
#[inline]
|
||||
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`].
|
||||
#[inline]
|
||||
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
|
||||
for<'a> F::Of<'a>: 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`].
|
||||
#[inline]
|
||||
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`].
|
||||
#[inline]
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user