Thread (9 messages) 9 messages, 3 authors, 2026-09-05

Re: [PATCH v2 1/2] rust: serdev: Fix race condition on driver unbind

From: Markus Probst <markus.probst@posteo.de>
Date: 2026-09-05 13:47:22
Also in: sashiko-reviews

On Sat, 2026-09-05 at 13:44 +0000, sashiko-bot@kernel.org wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The function `drvdata_drop` introduces a Use-After-Free window and mutable aliasing vulnerability by dropping the driver data before clearing `dev->driver_data`.
- [Low] The new helper function `drvdata_drop` is missing the `#[inline]` attribute.
--

commit 26ab6ef2b4c35bdc9d899d45004d0c1a7f3fca80
Author: Markus Probst [off-list ref]

rust: serdev: Fix race condition on driver unbind

This commit attempts to fix a race condition where the driver data pointer is
NULL while the serdev device is still open. It does this by introducing a new
function drvdata_drop() that leaves the pointer valid until the memory drop
is complete.

Closes: https://lore.kernel.org/linux-serial/20260905000836.C8FC91F00A3D@smtp.kernel.org/ (local)
quoted
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 2291d85b6849e..3886cc713c28b 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
[ ... ]
quoted
@@ -236,6 +237,32 @@ pub(crate) unsafe fn drvdata_obtain<T>(&self) -> Option<Pin<KBox<T>>> {
         //   in `into_foreign()`.
         Some(unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) })
     }
+
+    /// Drop the private data stored in this [`Device`].
+    ///
+    /// The pointer to the private data remains valid until the drop is complete.
+    ///
+    /// # Safety
+    ///
+    /// - The type `T` must match the type of the `ForeignOwnable` previously stored by
+    ///   [`Device::set_drvdata`].
+    pub(crate) unsafe fn drvdata_drop<T>(&self) {
[Severity: Low]
This isn't a bug, but should this abstraction function be annotated with
`#[inline]`?

The Rust subsystem coding guidelines mandate that small functions forwarding
to C bindings or performing small abstraction tasks should use `#[inline]`.
quoted
+        // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
+        let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) };
+
+        if ptr.is_null() {
+            return;
+        }
+
+        // SAFETY:
+        // - If `ptr` is not NULL, it comes from a previous call to `into_foreign()`.
+        // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()`
+        //   in `into_foreign()`.
+        drop(unsafe { Pin::<KBox<T>>::from_foreign(ptr.cast()) });
+
+        // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`.
+        unsafe { bindings::dev_set_drvdata(self.as_raw(), core::ptr::null_mut()) };
drvdata_obtain didn't have it.
[Severity: High]
Does this create a Use-After-Free window?

By dropping the driver data before clearing `dev->driver_data`, the pointer
remains valid while and after the memory is freed.

If a concurrent context, such as a hardware interrupt triggering
receive_buf_callback() in rust/kernel/serdev.rs, runs at this exact
moment, it will borrow a dangling pointer:

rust/kernel/serdev.rs:receive_buf_callback() {
    ...
    let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
    ...
}

Could this lead to a memory corruption vulnerability during the unbind path?
Under a normal case, no one should access this pointer anymore. For
serdev, this isn't a normal case. Before it becomes a dangling pointer,
PinnedDrop ensures it won't be used afterwards.

quoted
+    }
 }

Attachments

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help