Re: [PATCH RESEND v23 3/3] rust: leds: add multicolor classdev abstractions

[email protected] Wed, 05 Aug 2026 20:52:12 +0000
Newsgroups org.kernel.vger.linux-leds,org.kernel.vger.linux-pci
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(=
s) to consider:
- [High] Unsound fwnode reference counting and recovery logic leads to a me=
mory leak or use-after-free.
- [High] Unsynchronized concurrent reads of `brightness` and `intensity` cr=
eate a data race, resulting in Undefined Behavior.
- [Low] Commit message description is not capitalized, violating the LED su=
bsystem guidelines.
--

--- Patch [3]: [PATCH RESEND v23 3/3] rust: leds: add multicolor classdev a=
bstractions ---
commit 31ed8e2758580675a7a667a24dbbce311465d001
Author: Markus Probst <[email protected]>

rust: leds: add multicolor classdev abstractions

Implement the abstractions needed for multicolor led class devices,
including led::MultiColor, MultiColorSubLed, and led::MultiColorDevice.

> rust: leds: add multicolor classdev abstractions

[Severity: Low]
This isn't a bug, but the commit message description is not capitalized,
violating the LED subsystem guidelines. Should this be "Add multicolor
classdev abstractions"?

> diff --git a/rust/kernel/led/multicolor.rs b/rust/kernel/led/multicolor.rs
> new file mode 100644
> index 0000000000000..82fe96fd5273b
> --- /dev/null
> +++ b/rust/kernel/led/multicolor.rs
[ ... ]
> +    /// The intensity of the sub led.
> +    #[inline]
> +    pub const fn intensity(&self) -> u32 {
> +        // SAFETY:
> +        // - `self.intensity.get()` is a valid pointer to `u32`.
> +        // - We don't have exclusive or immutable access to `self.intens=
ity`,
> +        //   but the alignment should prevent "load tearing".
> +        unsafe { *self.intensity.get() }
> +    }
> +
> +    /// The brightness of the sub led.
> +    #[inline]
> +    pub const fn brightness(&self) -> u32 {
> +        // SAFETY:
> +        // - `self.brightness.get()` is a valid pointer to `u32`.
> +        // - We don't have exclusive or immutable access to `self.bright=
ness`,
> +        //   but the alignment should prevent "load tearing".
> +        unsafe { *self.brightness.get() }
> +    }

[Severity: High]
Does this code introduce undefined behavior due to unsynchronized concurrent
reads?

The C subsystem updates these fields concurrently when user-space writes to
sysfs. In Rust's memory model, plain pointer dereferences on non-atomic
variables that can be concurrently mutated constitute a data race and strict
undefined behavior.

Should this use core::ptr::read_volatile or atomics instead?

[ ... ]
> +                // SAFETY:
> +                // - `parent.as_ref().as_raw()` is guaranteed to be a po=
inter to a valid
> +                //    `device`.
> +                // - `ptr` is guaranteed to be a pointer to an initializ=
ed `led_classdev_mc`.
> +                to_result(unsafe {
> +                    bindings::led_classdev_multicolor_register_ext(
> +                        parent.as_ref().as_raw(),
> +                        ptr,
> +                        if self.name.is_none() {
> +                            &raw mut init_data
> +                        } else {
> +                            core::ptr::null_mut()
> +                        },
> +                    )
> +                })?;
> +
> +                subled_guard.dismiss();
> +
> +                core::mem::forget(self.fwnode); // keep the reference co=
unt incremented
> +
> +                Ok::<_, Error>(())
> +            }),

[Severity: High]
Can this code leak the fwnode reference?

If self.name is present, null_mut() is passed for init_data, meaning
the fwnode is never passed to the C subsystem. However, core::mem::forget()
is called unconditionally, leaking the reference since the device will not
have the fwnode for recovery during drop.

[ ... ]
> +#[pinned_drop]
> +impl<'bound, T: LedOps<Mode =3D MultiColor> + 'bound> PinnedDrop for Mul=
tiColorDevice<'bound, T> {
> +    fn drop(self: Pin<&mut Self>) {
> +        let raw =3D self.classdev.get();
> +        // SAFETY: The existence of `self` guarantees that `self.classde=
v.get()` is a pointer to a
> +        // valid `led_classdev_mc`.
> +        let dev: &device::Device =3D unsafe { device::Device::from_raw((=
*raw).led_cdev.dev) };
> +
> +        let _fwnode =3D dev
> +            .fwnode()
> +            // SAFETY: the reference count of `fwnode` has previously be=
en
> +            // incremented in `led::DeviceBuilder::build_multicolor`.
> +            .map(|fwnode| unsafe { ARef::from_raw(NonNull::from(fwnode))=
 });

[Severity: High]
Can this cause a use-after-free?

If self.fwnode was originally None, but the C subsystem matches and
assigns an fwnode from elsewhere (e.g. the parent device), this drop logic
will wrongly assume ownership of it and drop an unowned ARef.

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260805-rust_leds-=
[email protected]?part=3D3