From: sashiko-bot@kernel.org
To: "Markus Probst" <markus.probst@posteo.de>
Cc: ojeda@kernel.org, linux-serial@vger.kernel.org
Subject: Re: [PATCH 3/5] rust: serdev: Simplify callbacks
Date: Sun, 06 Sep 2026 16:13:58 +0000 [thread overview]
Message-ID: <20260906161359.713361F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260906-rust_serdev_probe_refactor-v1-3-69cdae0074ec@posteo.de>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] TOCTOU race condition causing a NULL pointer dereference in `receive_buf_callback` during device probe.
- [Low] Missing `#[inline]` annotations on abstraction functions `PrivateData::driver_data` and `OpenGuard::drop`.
--
commit 4244bd311fa3c091fa7a05ad05f18ce4e08829f6
Author: Markus Probst <markus.probst@posteo.de>
Subject: rust: serdev: Simplify callbacks
This commit simplifies serdev callbacks by initializing private data
directly on PrivateData. It also introduces OpenGuard for resource cleanup.
> diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
> index c16d6593a8d2d..66543108ec2fa 100644
> --- a/rust/kernel/serdev.rs
> +++ b/rust/kernel/serdev.rs
[ ... ]
> @@ -92,24 +88,35 @@ unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
> }
> }
>
> +struct OpenGuard<'bound> {
> + sdev: &'bound Device<device::Bound>,
> +}
> +
> +impl Drop for OpenGuard<'_> {
> + fn drop(&mut self) {
> + // SAFETY:
> + // - `self.sdev.as_raw()` is guaranteed to be a pointer to a valid
> + // `struct serdev_device`.
> + // - The existence of self proves that the device is open.
> + unsafe { bindings::serdev_device_close(self.sdev.as_raw()) };
> + }
> +}
[Severity: Low]
Does this drop implementation need an #[inline] annotation?
The Rust subsystem guidelines state that in abstractions like the kernel crate,
small functions or functions forwarding to a C binding call should be annotated
with #[inline].
> +
> #[doc(hidden)]
> -#[pin_data(PinnedDrop)]
> +#[pin_data]
> pub struct PrivateData<'bound, T: Driver> {
> - sdev: &'bound Device<device::Bound>,
> #[pin]
> - driver: UnsafeCell<MaybeUninit<T::Data<'bound>>>,
> - open: UnsafeCell<bool>,
> + driver: T::Data<'bound>,
> + open: OpenGuard<'bound>,
> }
>
> -#[pinned_drop]
> -impl<T: Driver> PinnedDrop for PrivateData<'_, T> {
> - fn drop(self: Pin<&mut Self>) {
> - // SAFETY: We have exclusive access to `self.open`.
> - if unsafe { *self.open.get() } {
> - // SAFETY: `self.sdev.as_raw()` is guaranteed to be a pointer to a valid
> - // `struct serdev_device`.
> - unsafe { bindings::serdev_device_close(self.sdev.as_raw()) };
> - }
> +impl<'bound, T: Driver> PrivateData<'bound, T> {
> + fn driver_data(self: Pin<&Self>) -> Pin<&T::Data<'bound>> {
> + // SAFETY: We treat the result as pinned.
> + let inner = unsafe { Pin::into_inner_unchecked(self) };
> +
> + // SAFETY: `self.driver` is pinned.
> + unsafe { Pin::new_unchecked(&inner.driver) }
> }
> }
[Severity: Low]
Does this helper function also need an #[inline] annotation?
Similar to OpenGuard::drop above, this appears to be a small projection
function in an abstraction crate, which the subsystem guidelines suggest
annotating with #[inline].
> @@ -134,46 +141,30 @@ extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi:
>
> from_result(|| {
> sdev.as_ref().set_drvdata(try_pin_init!(PrivateData::<T> {
> - sdev: &**sdev,
> - driver: MaybeUninit::<T::Data<'_>>::zeroed().into(),
> - open: false.into(),
> + open: {
> + // SAFETY:
> + // - `sdev.as_raw()` is guaranteed to be a valid pointer to
> + // `serdev_device`.
> + // - It is safe to call before open.
> + unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
> +
> + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to
> + // `serdev_device`.
> + unsafe { bindings::serdev_device_pause_rx(sdev.as_raw()) };
> +
> + // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to
> + // `serdev_device`.
> + to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?;
> +
> + OpenGuard { sdev }
> + },
> + driver <- T::probe(sdev, info),
> }))?;
[Severity: High]
Could this sequence introduce a race condition that leads to a NULL pointer
dereference in receive_buf_callback() during device probe?
Because bindings::serdev_device_open() is called inside try_pin_init!(), the
device is exposed to hardware interrupts before the initialized struct is
passed to set_drvdata(). This means dev->driver_data remains NULL during this
window.
While bindings::serdev_device_pause_rx() is called before open, and
bindings::serdev_device_resume_rx() is called after set_drvdata() below,
resume_rx() uses clear_bit(SERPORT_PAUSE_RX) which lacks a memory barrier.
On weakly-ordered CPU architectures, can the CPU reorder the store to
dev->driver_data to happen after the atomic clear?
If a concurrent RX worker thread observes SERPORT_PAUSE_RX == 0 while
dev->driver_data is still NULL, it could trigger receive_buf_callback() and
crash when dereferencing the NULL pointer.
> - // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> - let private_data = unsafe { sdev.as_ref().drvdata_borrow::<PrivateData<'_, T>>() };
> - let private_data = ScopeGuard::new_with_data(private_data, |_| {
> - // SAFETY: We just set drvdata to `PrivateData<'_, T>`.
> - drop(unsafe { sdev.as_ref().drvdata_obtain::<PrivateData<'_, T>>() });
> - });
> - // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> - unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
>
> // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> - unsafe { bindings::serdev_device_pause_rx(sdev.as_raw()) };
> -
> - // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
> - // to a `serdev_device`.
> - to_result(unsafe { bindings::serdev_device_open(sdev.as_raw()) })?;
> -
> - // SAFETY: We have exclusive access to `private_data.open`.
> - unsafe { *private_data.open.get() = true };
> -
> - let data = T::probe(sdev, info);
> + unsafe { bindings::serdev_device_resume_rx(sdev.as_raw()) };
>
> - // SAFETY: We have exclusive access to `private_data.driver`.
> - let driver = unsafe { &mut *private_data.driver.get() };
> - // SAFETY:
> - // - `driver.as_mut_ptr()` is a valid pointer to uninitialized data.
> - // - `private_data.driver` is pinned.
> - let result = unsafe { pin_init::raw_try_init(driver.as_mut_ptr(), data) };
> -
> - result.map(|()| {
> - private_data.dismiss();
> -
> - // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
> - unsafe { bindings::serdev_device_resume_rx(sdev.as_raw()) };
> -
> - 0
> - })
> + Ok(0)
> })
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260906-rust_serdev_probe_refactor-v1-0-69cdae0074ec@posteo.de?part=3
next prev parent reply other threads:[~2026-09-06 16:13 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-06 15:55 [PATCH 0/5] rust: serdev: Refactor Markus Probst
2026-09-06 15:55 ` [PATCH 1/5] tty: serdev: Export functions to pause receive_buf callback calls Markus Probst
2026-09-06 16:08 ` sashiko-bot
2026-09-06 15:55 ` [PATCH 2/5] rust: serdev: Replace `active` mutex with receive pause Markus Probst
2026-09-06 16:09 ` sashiko-bot
2026-09-06 15:55 ` [PATCH 3/5] rust: serdev: Simplify callbacks Markus Probst
2026-09-06 16:13 ` sashiko-bot [this message]
2026-09-06 15:55 ` [PATCH 4/5] rust: Add `Device::drvdata_borrow_mut` Markus Probst
2026-09-06 16:08 ` sashiko-bot
2026-09-06 15:55 ` [PATCH 5/5] rust: serdev: Pause receive callback before calling unbind Markus Probst
2026-09-06 16:11 ` sashiko-bot
2026-09-06 16:20 ` Danilo Krummrich
2026-09-06 17:36 ` Markus Probst
2026-09-06 20:13 ` Gary Guo
2026-09-06 22:51 ` Markus Probst
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=20260906161359.713361F00A3A@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=markus.probst@posteo.de \
--cc=ojeda@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