From: Danilo Krummrich <dakr@kernel.org>
To: gregkh@linuxfoundation.org, rafael@kernel.org, dakr@kernel.org,
ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net,
bjorn3_gh@protonmail.com, a.hindborg@kernel.org,
aliceryhl@google.com, tmgross@umich.edu,
daniel.almeida@collabora.com, tamird@kernel.org,
acourbot@nvidia.com, work@onurozkan.dev, lyude@redhat.com
Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org, stable@vger.kernel.org,
Sashiko <sashiko-bot@kernel.org>
Subject: [PATCH] rust: devres: fix race between concurrent revokers
Date: Sun, 28 Jun 2026 19:44:38 +0200 [thread overview]
Message-ID: <20260628174451.2275679-1-dakr@kernel.org> (raw)
There is a potential race condition when two paths try to revoke a
Devres concurrently.
The driver core's devres_release_all() calls Revocable::revoke() via the
release callback, while Devres::drop() calls revoke_nosync() on another
CPU.
The revoker that does not claim the is_available swap returns
immediately, but the revoker that did may still be executing
drop_in_place() on the inner data. This can cause a use-after-free when
the other revoker's caller proceeds to drop adjacent resources that
drop_in_place() still references (e.g., Devres<DmaMappedSgt> racing with
SGTable freeing the backing sg_table and pages).
Fix this by adding a Completion. The release callback signals the
Completion after revoke() finishes, and Devres::drop() waits for it when
it loses the is_available swap. This ensures the wrapped object is fully
torn down before Devres::drop() returns.
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/dri-devel/20260612202841.2577C1F000E9@smtp.kernel.org/
Fixes: 05aa6fb1c21d ("rust: scatterlist: Add abstraction for sg_table")
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/devres.rs | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 11ce500e9b76..11d862f1e6de 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -21,7 +21,8 @@
sync::{
aref::ARef,
rcu,
- Arc, //
+ Arc,
+ Completion, //
},
types::{
ForeignOwnable,
@@ -37,6 +38,8 @@ struct Inner<T> {
node: Opaque<bindings::devres_node>,
#[pin]
data: Revocable<T>,
+ #[pin]
+ revocation: Completion,
}
/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
@@ -53,6 +56,10 @@ struct Inner<T> {
/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
/// anymore.
///
+/// When a [`Devres`] is dropped, it is guaranteed that `T` has been fully dropped by the time
+/// [`Devres::drop`] returns, even if a concurrent revocation through the release callback is in
+/// progress.
+///
/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
/// [`Drop`] implementation.
///
@@ -217,6 +224,7 @@ pub fn new<E>(dev: &Device<Bound>, data: impl PinInit<T, E>) -> Result<Self>
};
}),
data <- Revocable::new(data),
+ revocation <- Completion::new(),
}),
GFP_KERNEL,
)?;
@@ -254,7 +262,9 @@ fn data(&self) -> &Revocable<T> {
// SAFETY: `inner` is a valid `Inner<T>` pointer.
let inner = unsafe { &*inner };
- inner.data.revoke();
+ if inner.data.revoke() {
+ inner.revocation.complete_all();
+ }
}
#[allow(clippy::missing_safety_doc)]
@@ -361,6 +371,10 @@ fn drop(&mut self) {
// this additional reference count.
drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.inner)) });
}
+ } else {
+ // The release callback is concurrently revoking; wait for it to finish
+ // `drop_in_place()` of the wrapped object before returning.
+ self.inner.revocation.wait_for_completion();
}
}
}
base-commit: 0716f9b9338a86dd27796e00ed0fd560c653323a
--
2.54.0
next reply other threads:[~2026-06-28 17:45 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-28 17:44 Danilo Krummrich [this message]
2026-06-28 20:02 ` [PATCH] rust: devres: ensure revocation is complete before device finishes unbinding Danilo Krummrich
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=20260628174451.2275679-1-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sashiko-bot@kernel.org \
--cc=stable@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.