dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Philipp Stanner" <phasta@kernel.org>
Cc: dri-devel@lists.freedesktop.org, ojeda@kernel.org
Subject: Re: [PATCH v5 4/5] rust: Add dma_fence abstractions
Date: Fri, 03 Jul 2026 07:45:13 +0000	[thread overview]
Message-ID: <20260703074514.9CB5E1F00A3D@smtp.kernel.org> (raw)
In-Reply-To: <20260703073141.3962604-6-phasta@kernel.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Lifetime erasure via `ForeignOwnable` or `core::mem::forget` on `DriverFence` leads to a Use-After-Free of `FenceCtx`.
- [Medium] `DriverFence::signal` and `DriverFence::drop` omit the required `dma_fence_begin_signalling()` and `dma_fence_end_signalling()` lockdep annotations.
- [Low] `FenceCbRegistration`'s `# Invariants` documentation is outdated and lacks the mandatory `// INVARIANT:` comment upon construction.
--

commit d72bac0aa1194434de7bc7000b2d5a50bb882d68
Author: Philipp Stanner <phasta@kernel.org>

rust: Add dma_fence abstractions

This commit adds Rust abstractions for the dma_fence subsystem, providing
DriverFence and Fence types to cleanly decouple fence issuers and consumers
while ensuring safe reference counting and signaling.

> diff --git a/rust/kernel/dma_buf/dma_fence.rs b/rust/kernel/dma_buf/dma_fence.rs
> --- /dev/null
> +++ b/rust/kernel/dma_buf/dma_fence.rs
[ ... ]
> +/// # Invariants
> +///
> +/// If callback is Some, then cb is registered with the fence and the
> +/// callback hasn't been invoked yet. If None, the callback has been invoked
> +/// or the fence was already signaled when we tried to register.
> +#[pin_data(PinnedDrop)]
> +pub struct FenceCbRegistration<T: FenceCb + 'static> {
> +    #[pin]
> +    callback_foreign: Opaque<bindings::dma_fence_cb>,
> +    callback: ManuallyDrop<T>,

[Severity: Low]
Is this documentation accurate?

The invariants description references Some and None for the callback
field, but the field is defined as ManuallyDrop<T> rather than an Option.

[ ... ]
> +    pub fn new<'a>(fence: &'a Fence, callback: T) -> impl PinInit<Self, CallbackError<T>> + 'a
> +    where
> +        T: 'a,
> +    {
> +        try_pin_init!(Self {
> +            // We need to fully initialize the fence because after
> +            // dma_fence_add_callback() ran, the callback might immediately
> +            // get invoked.
> +            callback: ManuallyDrop::new(callback),

[Severity: Low]
Are we missing the mandatory INVARIANT comment here?

The Rust subsystem guidelines require an explicit INVARIANT comment when
constructing a struct with an Invariants documentation section, but it
appears to be omitted here.

[ ... ]
> +    pub fn signal(self, res: Result) {
> +        let fence = self.as_raw();
> +        let mut fence_flags: usize = 0;
> +        let flag_ptr = &raw mut fence_flags;
> +
> +        // SAFETY: fence is valid because self is valid. flag_ptr is
> +        // merely a pointer to an integer, which lives as long as this function.
> +        unsafe { bindings::dma_fence_lock_irqsave(fence, flag_ptr) };
> +        // SAFETY: fence is valid because self is valid. The lock must be
> +        // held, which we acquired directly above.
> +        if !unsafe { bindings::dma_fence_test_signaled_flag(fence) } {
> +            if let Err(err) = res {
> +                // SAFETY: fence is valid because self is valid. The fence
> +                // must not have been signaled yet, which we check directly above.
> +                unsafe { bindings::dma_fence_set_error(fence, err.to_errno()) };
> +            }
> +            // SAFETY: fence is valid because self is valid. The lock must
> +            // be held, which we acquired above.
> +            unsafe { bindings::dma_fence_signal_locked(fence) };
> +        }
> +        // SAFETY: fence is valid because self is valid. flag_ptr is
> +        // merely a pointer to an integer, which lives as long as this function.
> +        // The lock must be held, which we acquired above.
> +        unsafe { bindings::dma_fence_unlock_irqrestore(fence, flag_ptr) };
> +    }

[Severity: Medium]
Should this manual signaling path include lockdep annotations?

By open-coding the fence signaling using dma_fence_lock_irqsave() and
dma_fence_signal_locked(), this function omits the
dma_fence_begin_signalling() and dma_fence_end_signalling() calls used in
the equivalent C dma_fence_signal() function.

Without these annotations, lockdep cannot track cross-release dependencies
during fence signaling, which might lead to false negatives when detecting
deadlocks between fence signaling and wait paths.

[ ... ]
> +unsafe impl<'b, T: Send + Sync + FenceCtxOps + 'static> ForeignOwnable for DriverFence<'b, T> {

[Severity: High]
Could this lead to a Use-After-Free if the DriverFence is leaked?

Since DriverFence implements ForeignOwnable without requiring a 'static
lifetime for the 'b parameter, a user can call into_foreign() on a
DriverFence<'a, T> or use core::mem::forget() to leak it.

If the DriverFence is leaked, DriverFence::drop() is never called.
The Rust compiler will then allow the borrowed FenceCtx (tied to
lifetime 'a in DriverFenceData) to be dropped. Once FenceCtx::drop()
completes, the driver_name is freed.

However, the C backend could later interact with the dma_fence, which
is kept alive by ARef<Fence>, and invoke get_driver_name(). This would
dereference the dangling fctx pointer, causing a Use-After-Free.

[ ... ]
> +    fn drop(&mut self) {
> +        let fence = self.as_raw();
> +        let mut fence_flags: usize = 0;
> +        let flag_ptr = &raw mut fence_flags;
> +
> +        // SAFETY: fence is valid until the call_rcu() below. flag_ptr is
> +        // merely a pointer to an integer, which lives as long as this function.
> +        unsafe { bindings::dma_fence_lock_irqsave(fence, flag_ptr) };
> +
> +        // Use dma_fence_test_signaled_flag() instead of dma_fence_is_signaled_locked()
> +        // because the C backend wants to get rid of the latter.
> +
> +        // SAFETY: fence is valid until the call_rcu() below.
> +        let signaled: bool = unsafe { bindings::dma_fence_test_signaled_flag(fence) };
> +        if warn_on!(!signaled) {
> +            // SAFETY: fence is valid until the call_rcu() below. The fence
> +            // must not have been signaled yet, which we check directly above.
> +            unsafe { bindings::dma_fence_set_error(fence, ECANCELED.to_errno()) };
> +            // SAFETY: fence is valid until the call_rcu() below. The lock must
> +            // be held, which we acquired above.
> +            unsafe { bindings::dma_fence_signal_locked(fence) };
> +        }
> +        // SAFETY: fence is valid until the call_rcu() below. flag_ptr is
> +        // merely a pointer to an integer, which lives as long as this function.
> +        // The lock must be held, which we acquired above.
> +        unsafe { bindings::dma_fence_unlock_irqrestore(fence, flag_ptr) };

[Severity: Medium]
Does this cancellation path also need the lockdep annotations?

Similar to DriverFence::signal(), this open-coded signaling omits the
dma_fence_begin_signalling() and dma_fence_end_signalling() lockdep wrappers,
which prevents cross-release dependency tracking.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260703073141.3962604-2-phasta@kernel.org?part=4

  reply	other threads:[~2026-07-03  7:45 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03  7:31 [PATCH v5 0/5] rust / dma_buf: Add abstractions for dma_fence Philipp Stanner
2026-07-03  7:31 ` [PATCH v5 1/5] rust: types: implement ForeignOwnable for ARef<T> Philipp Stanner
2026-07-03  7:42   ` sashiko-bot
2026-07-03  7:31 ` [PATCH v5 2/5] rust: error: Add ECANCELED error code Philipp Stanner
2026-07-03  7:31 ` [PATCH v5 3/5] rust: sync: Add abstraction for rcu_barrier() Philipp Stanner
2026-07-03  7:40   ` sashiko-bot
2026-07-03  7:31 ` [PATCH v5 4/5] rust: Add dma_fence abstractions Philipp Stanner
2026-07-03  7:45   ` sashiko-bot [this message]
2026-07-03  7:31 ` [PATCH v5 5/5] MAINTAINERS: Add entry for Rust dma-buf Philipp Stanner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260703074514.9CB5E1F00A3D@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=ojeda@kernel.org \
    --cc=phasta@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox