Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Markus Probst" <markus.probst@posteo.de>
Cc: linux-pci@vger.kernel.org, ojeda@kernel.org,
	linux-leds@vger.kernel.org, lee@kernel.org
Subject: Re: [PATCH v21 1/3] rust: leds: add basic led classdev abstractions
Date: Mon, 29 Jun 2026 13:36:27 +0000	[thread overview]
Message-ID: <20260629133627.A68C11F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260629-rust_leds-v21-1-4f0f19579db5@posteo.de>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The `blink_set_callback` creates multiple mutable references (`&mut usize`) to the same shared memory concurrently, violating Rust's strict aliasing rules.
- [High] The `DeviceBuilder::build` method erases the lifetime of the `name` string, allowing the `led_classdev.name` pointer to outlive the string and causing a Use-After-Free.
- [High] Unsound `FwNode` reference count management in `Device::drop` retrieves the `fwnode` directly from the mutable C structure, leading to refcount underflow and Use-After-Free.
--

commit d59e3e50bab36d2a8936041aa09ed5823876969f
Author: Markus Probst <markus.probst@posteo.de>

rust: leds: add basic led classdev abstractions

Implement the core abstractions needed for led class devices, including
LedOps, DeviceBuilder, and Device.

> diff --git a/rust/kernel/led/normal.rs b/rust/kernel/led/normal.rs
> new file mode 100644
> index 0000000000000..2769f690bb245
> --- /dev/null
> +++ b/rust/kernel/led/normal.rs

[ ... ]

> +impl<'a, S: DeviceBuilderState> DeviceBuilder<'a, S> {
> +    /// Registers a new [`Device`].
> +    pub fn build<'bound: 'a, T: LedOps + 'bound>(
> +        self,
> +        parent: &'bound T::Bus,
> +        ops: impl PinInit<T, Error> + 'a,
> +    ) -> impl PinInit<Device<'bound, T>, Error> + 'a {

[Severity: High]
Is the lifetime bound <'bound: 'a> backwards here?

By specifying 'bound: 'a, the returned Device ('bound) is explicitly permitted
to outlive the builder's string lifetime ('a).

Because the raw pointer to this string is written directly into the
led_classdev.name field, a driver could initialize a Device with a temporary
string, which drops the string while leaving led_cdev->name dangling. Can
this lead to a use-after-free?

[ ... ]

> +    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) };
> +            // SAFETY: `classdev.parent()` is guaranteed to be contained in `T::Bus`.
> +            let parent = unsafe { T::Bus::from_device(classdev.parent()) };
> +
> +            classdev.ops.blink_set(
> +                parent,
> +                classdev,
> +                // 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]
Could this lead to a strict aliasing violation by creating multiple mutable
references to the same shared memory concurrently?

In the C LED subsystem, sysfs write operations to delay_on and delay_off can
invoke led_blink_set() concurrently across different threads without holding
locks on the device.

This passes pointers to the shared led_cdev->blink_delay_on and
led_cdev->blink_delay_off fields directly into the blink_set callback. By
unconditionally casting these raw C pointers to &mut usize in Rust, the
callback can create overlapping mutable references to the exact same memory
locations.

[ ... ]

> +#[pinned_drop]
> +impl<'bound, T: LedOps + '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 unsoundly steal a reference count from the C field dev->fwnode?

In DeviceBuilder::build, self.fwnode is passed to C and its refcount is
leaked via core::mem::forget. However, if self.fwnode was initially None, no
reference count is leaked.

If dev->fwnode is subsequently populated by external C code, this
PinnedDrop implementation blindly constructs an ARef and drops it. Since
Rust never incremented this refcount, could this result in an underflow
and an eventual use-after-free of the FwNode?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260629-rust_leds-v21-0-4f0f19579db5@posteo.de?part=1

  reply	other threads:[~2026-06-29 13:36 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-29 13:10 [PATCH v21 0/3] rust: leds: add led classdev abstractions Markus Probst
2026-06-29 13:10 ` [PATCH v21 1/3] rust: leds: add basic " Markus Probst
2026-06-29 13:36   ` sashiko-bot [this message]
2026-06-29 13:10 ` [PATCH v21 2/3] rust: leds: add Mode trait Markus Probst
2026-06-29 13:40   ` sashiko-bot
2026-06-29 13:10 ` [PATCH v21 3/3] rust: leds: add multicolor classdev abstractions Markus Probst
2026-06-29 13:55   ` sashiko-bot

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=20260629133627.A68C11F00A3A@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox