Re: [PATCH] rust: DmaFence: Remove static lifetime
From: Gary Guo
Date: Tue Sep 22 2026 - 06:16:17 EST
On Tue Sep 22, 2026 at 9:36 AM BST, Philipp Stanner wrote:
> A FenceCallbackRegistration can stem from another party than the one
> that has created a Fence. Should that party forget the registration
> object (for example through a refcount cycle) and then unload the
> module, a fence signaling would run into the unloaded module, causing
> UAF bugs.
>
> So far, this has been solved by demanding that the payload data of the
> registration object demanding static lifetime.
>
> It turns out, however, that this is harmful because the static lifetime
> bubbles up to all users, ultimately potentially causing a large amount
> of driver data to be static, which renders the lifetime obsolete.
>
> Solve this issue instead through an unsafe requirement which demands
> that the user does not forget the registration object. This is also the
> solution chosen by ScopedWork.
>
> Suggested-by: Danilo Krummrich <dakr@xxxxxxxxxx>
> Signed-off-by: Philipp Stanner <phasta@xxxxxxxxxx>
> ---
> rust/kernel/dma_buf/dma_fence.rs | 16 ++++++++++++----
> 1 file changed, 12 insertions(+), 4 deletions(-)
>
> diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
> index 18a43e1bb442..c3fa68c4df86 100644
> --- a/rust/kernel/dma_buf/dma_fence.rs
> +++ b/rust/kernel/dma_buf/dma_fence.rs
> @@ -291,7 +291,7 @@ fn from(e: AllocError) -> Self {
> /// }
> /// }
> /// ```
> -pub trait FenceCallback: Send + 'static {
> +pub trait FenceCallback: Send {
> /// Called when the fence is signaled.
> ///
> /// This is called from the fence signaling path, which may be in interrupt
> @@ -310,7 +310,7 @@ pub trait FenceCallback: Send + 'static {
> /// When this object is dropped, the callback is automatically removed if it
> /// hasn't been called yet.
> #[pin_data(PinnedDrop)]
> -pub struct FenceCallbackRegistration<T: FenceCallback + 'static> {
> +pub struct FenceCallbackRegistration<T: FenceCallback> {
> #[pin]
> callback_foreign: Opaque<bindings::dma_fence_cb>,
> callback: ManuallyDrop<T>,
> @@ -326,7 +326,14 @@ impl<T: FenceCallback> FenceCallbackRegistration<T> {
> /// On success the callback is pinned in place and will fire when the fence
> /// signals. On `AlreadySignaled` the callback is returned to the caller so
> /// that owned resources can be reclaimed.
> - pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit<Self, CallbackError<T>> + 'a
> + ///
> + /// # Safety
> + ///
> + /// `callback` must not be forgotten.
This should rather say "the callback registration" must not be forgotten (i.e.
not the `callback` parameter, but the constructed init value).
Best,
Gary
> + pub unsafe fn new<'a>(
> + fence: &'a Fence,
> + callback: T,
> + ) -> impl PinInit<Self, CallbackError<T>> + 'a
> where
> T: 'a,
> {
> @@ -693,7 +700,8 @@ struct DriverFenceData<'a, T: Send + Sync + FenceContextOps> {
> ///
> /// let cb_data = CallbackData { };
> /// let waiting_fence = ARef::from(fence.as_fence());
> -/// let cb_reg = FenceCallbackRegistration::new(&waiting_fence, cb_data);
> +/// // SAFETY: `cb_data`'s content is not forgotten.
> +/// let cb_reg = unsafe { FenceCallbackRegistration::new(&waiting_fence, cb_data) };
> /// let cb_reg = KBox::pin_init(cb_reg, GFP_KERNEL)?;
> ///
> /// // TODO signalling guards