From: sashiko-bot@kernel.org
To: "Markus Probst" <markus.probst@posteo.de>
Cc: lee@kernel.org, linux-leds@vger.kernel.org, ojeda@kernel.org,
gary@garyguo.net, linux-pci@vger.kernel.org
Subject: Re: [PATCH v26 1/4] rust: leds: Add basic led classdev abstractions
Date: Wed, 30 Sep 2026 13:18:40 +0000 [thread overview]
Message-ID: <20260930131840.D44F71F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260930-rust_leds-v26-1-83837331020e@posteo.de>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] leds: mutable aliasing UB in blink_set_callback via concurrent sysfs
- [High] leds: lifetime inversion in DeviceBuilder allows Device to outlive strings
- [High] leds: FwNode use-after-free and leak via incorrect drop logic
- [Low] leds: missing #[inline] on Deref implementations for abstractions
--
commit 29180add5d0ac2c1b691782a2890b1e8097a96b7
Author: Markus Probst <markus.probst@posteo.de>
rust: leds: Add basic led classdev abstractions
This commit implements the core Rust abstractions for LED class devices,
including led::LedOps, led::DeviceBuilder, and led::Device.
> diff --git a/rust/kernel/led/normal.rs b/rust/kernel/led/normal.rs
> new file mode 100644
> index 0000000000000..a22c29cfd2628
> --- /dev/null
> +++ b/rust/kernel/led/normal.rs
[ ... ]
> +impl<'init, S: DeviceBuilderState> DeviceBuilder<'init, S> {
> + /// Registers a new [`Device`].
> + pub fn build<'bound: 'init, T: LedOps + 'bound>(
[Severity: High]
Can this lifetime bound cause a use-after-free? With <'bound: 'init>, the
returned Device<'bound> outlives the temporary strings borrowed in
DeviceBuilder<'init>.
If the safe driver uses temporary strings (like name and devicename) and builds
the device, they are written as raw pointers into the C structure. Since the
returned Device outlives them, the C subsystem could later dereference
dangling pointers if triggers access led_cdev->name.
Should this be inverted to <'init: 'bound>?
> + self,
> + parent: &'bound device::Device<Bound>,
> + ops: impl PinInit<T, Error> + 'init,
> + ) -> impl PinInit<Device<'bound, T>, Error> + 'init {
[ ... ]
> +impl<'bound, T: LedOps + 'bound> Deref for Device<'bound, T> {
> + type Target = T;
> +
> + fn deref(&self) -> &Self::Target {
> + &self.ops
> + }
> +}
[Severity: Low]
This isn't a bug, but this small abstraction function is missing the
recommended #[inline] attribute. The Rust subsystem coding guidelines mandate
that small abstraction functions, such as Deref trait implementations, must be
annotated with #[inline] to ensure proper compiler optimization across crate
boundaries.
[ ... ]
> + /// # Safety
> + /// `led_cdev` must be a valid pointer to a `led_classdev` embedded within a
> + /// `led::Device`.
> + /// `delay_on` and `delay_off` must be valid pointers to `usize` and have
> + /// exclusive access for the period of this function.
> + /// This function is called on enabling hardware accelerated blinking.
> + unsafe extern "C" fn blink_set_callback(
> + led_cdev: *mut bindings::led_classdev,
> + delay_on: *mut usize,
> + delay_off: *mut usize,
> + ) -> i32 {
> + from_result(|| {
> + // SAFETY: The function's contract guarantees that `led_cdev` is a valid pointer to a
> + // `led_classdev` embedded within a `led::Device`.
> + let classdev = unsafe { Device::<T>::from_raw(led_cdev) };
> +
> + classdev.blink_set(
> + // SAFETY: The function's contract guarantees that `delay_on` points to a `usize`
> + // and is exclusive for the period of this function.
> + unsafe { &mut *delay_on },
> + // SAFETY: The function's contract guarantees that `delay_off` points to a `usize`
> + // and is exclusive for the period of this function.
> + unsafe { &mut *delay_off },
> + )?;
[Severity: High]
Are we sure these mutable references have exclusive access?
The sysfs delay_on and delay_off attributes do not use locking to serialize
writes (for example, in led_delay_on_store() in ledtrig-timer.c). Concurrent
writes from user-space can cause the C core to invoke led_blink_set()
concurrently on multiple threads, passing pointers to shared fields like
&led_cdev->blink_delay_off.
If blink_set_callback() is invoked simultaneously on multiple threads, each
thread will cast the same shared pointer into &mut usize, creating overlapping
mutable references. This violates Rust's exclusive access guarantees and causes
Undefined Behavior.
> + Ok(0)
> + })
> + }
> +}
> +
> +#[pinned_drop]
> +impl<'bound, T: 'bound> PinnedDrop for Device<'bound, T> {
> + fn drop(self: Pin<&mut Self>) {
> + let raw = self.classdev.get();
> + // SAFETY: The existence of `self` guarantees that `self.classdev.get()` is a pointer to a
> + // valid `led_classdev`.
> + let dev: &device::Device = unsafe { device::Device::from_raw((*raw).dev) };
> +
> + let _fwnode = dev
> + .fwnode()
> + // SAFETY: the reference count of `fwnode` has previously been
> + // incremented in `led::DeviceBuilder::build`.
> + .map(|fwnode| unsafe { ARef::from_raw(NonNull::from(fwnode)) });
[Severity: High]
Does this drop logic correctly match the FwNode from the builder?
The builder intentionally leaks the Rust FwNode reference using
core::mem::forget(self.fwnode). However, here we unconditionally extract
dev.fwnode() from the C struct and cast it back to ARef<FwNode> to drop it.
If the builder was called with fwnode: None, it forgets None. But if the
C subsystem later attached an fwnode to the device (or if it was modified),
this drop logic will decrement the refcount of an FwNode it never owned,
causing a use-after-free. Additionally, if the C subsystem replaced the
original FwNode, the original node is leaked.
Should the driver store Option<ARef<FwNode>> explicitly within its Rust Device
struct instead of pulling it from the C state?
> +
> + // SAFETY: The existence of `self` guarantees that `self.classdev` has previously been
> + // successfully registered with `led_classdev_register_ext`.
> + unsafe { bindings::led_classdev_unregister(raw) };
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260930-rust_leds-v26-0-83837331020e@posteo.de?part=1
next prev parent reply other threads:[~2026-09-30 13:18 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 13:05 [PATCH v26 0/4] rust: leds: Add led classdev abstractions Markus Probst
2026-09-30 13:05 ` [PATCH v26 1/4] rust: leds: Add basic " Markus Probst
2026-09-30 13:18 ` sashiko-bot [this message]
2026-09-30 13:05 ` [PATCH v26 2/4] rust: leds: Add Mode trait Markus Probst
2026-09-30 13:10 ` sashiko-bot
2026-09-30 13:05 ` [PATCH v26 3/4] rust: leds: Add multicolor classdev abstractions Markus Probst
2026-09-30 13:17 ` sashiko-bot
2026-10-09 18:52 ` Boqun Feng
2026-10-09 19:18 ` Markus Probst
2026-10-09 19:34 ` Boqun Feng
2026-10-09 19:59 ` Markus Probst
2026-10-09 20:08 ` Boqun Feng
2026-10-09 22:11 ` leds: KCSAN report Markus Probst
2026-10-12 14:50 ` Boqun Feng
2026-09-30 13:05 ` [PATCH v26 4/4] MAINTAINERS: rust: leds: Add rust abstraction entry Markus Probst
2026-10-08 11:32 ` [PATCH v26 0/4] rust: leds: Add led classdev abstractions 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=20260930131840.D44F71F000FF@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=gary@garyguo.net \
--cc=lee@kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=linux-pci@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 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.