Re: [PATCH v16 3/3] rust: leds: add multicolor classdev abstractions
Markus Probst <[email protected]>
| Newsgroups | dev.linux.lists.sashiko |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2026-05-01 at 16:00 +0000, [email protected] wrote: > Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider: > - [Critical] Information Leak and Array Out-of-Bounds via FFI Layout Mismatch > - [High] Data Race and Undefined Behavior via Aliasing Violation on subled fields > - [High] Memory Leak and Use-After-Free via FwNode Reference Mismanagement > - [High] Undefined Behavior and Memory Corruption via `KVec` Capacity Mismatch > - [Low] Missing `#[inline]` attribute on `MultiColorDevice::from_raw` > -- > > commit 519046430edb434e8aa4a9b617987bbaa0db6c71 > Author: Markus Probst <[email protected]> > Subject: rust: leds: add multicolor classdev abstractions > > This commit implements the abstractions needed for multicolor led class > devices. It provides types for the multicolor led mode, the subleds, and > the device builder to register a new multicolor led class device. > > > diff --git a/rust/kernel/led/multicolor.rs b/rust/kernel/led/multicolor.rs > > new file mode 100644 > > index 000000000000..900ef050d2d3 > > --- /dev/null > > +++ b/rust/kernel/led/multicolor.rs > > @@ -0,0 +1,405 @@ > > [ ... ] > > > +#[repr(C)] > > +#[derive(Copy, Clone, Debug)] > > +#[non_exhaustive] > > +pub struct MultiColorSubLed { > > + /// the color of the sub led > > + pub color: Color, > > Does this structure have an identical memory layout to the C mc_subled > struct? The Color enum doesn't have a #[repr(u32)] or #[repr(C)] > attribute, so the Rust compiler will likely size it as 1 byte. > > To align the subsequent brightness field, the compiler would insert 3 bytes > of uninitialized padding. > > When the C LED core accesses mc_led_cdev->subled_info[i].color_index (which > is 4 bytes), could it read the 1-byte color value combined with the > uninitialized padding? This might produce a large index on little-endian > architectures, potentially leading to out-of-bounds reads when the C > subsystem indexes arrays like led_colors. Thats a new one. false positive. `Color` does have a #[repr(u32)]. Thanks - Markus Probst > > [ ... ] > > > + pub fn build_multicolor<T: LedOps<Mode = MultiColor>>( > [ ... ] > > + let mut subleds_vec = KVec::new(); > > + subleds_vec.extend_from_slice(subleds, GFP_KERNEL)?; > > + let (subled_info, num_colors, capacity) = subleds_vec.into_raw_parts(); > > + debug_assert_eq!(num_colors, capacity); > > Could this assertion fail if the allocator sizes the allocation larger than > num_colors? extend_from_slice can yield a capacity strictly larger than > the requested size. > > > + > > + let subled_guard = ScopeGuard::new(|| { > > + // SAFETY: `subled_info` is guaranteed to be a valid array pointer to > > + // `mc_subled` with the length and capacity of `num_colors`. > > + drop(unsafe { KVec::from_raw_parts(subled_info, num_colors, num_colors) }); > > + }); > > Is it safe to reconstruct the vector with num_colors as the capacity? > > If extend_from_slice originally allocated a capacity greater than > num_colors, passing a mismatched capacity back to the allocator during > cleanup could lead to memory corruption. > > [ ... ] > > > + // SAFETY: > > + // - `parent.as_ref().as_raw()` is guaranteed to be a pointer to a valid > > + // `device`. > > + // - `ptr` is guaranteed to be a pointer to an initialized `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 count incremented > > Does this reliably transfer the reference count to the C subsystem? > > If self.name.is_some(), the init_data pointer is explicitly passed as > NULL to led_classdev_multicolor_register_ext. This means the C device is > never associated with the fwnode, and the reference count leaked here might > never be recovered since PinnedDrop uses dev.fwnode() to clean it up. > > Conversely, if self.fwnode was None initially, but the C device inherited > an fwnode from its parent, would the PinnedDrop implementation reconstruct > an ARef from it and erroneously decrement a reference count it never owned? > > [ ... ] > > > +impl<T: LedOps<Mode = MultiColor>> MultiColorDevice<T> { > > + /// # Safety > > + /// `led_cdev` must be a valid pointer to a `led_classdev` embedded within a > > + /// `led::MultiColorDevice`. > > + unsafe fn from_raw<'a>(led_cdev: *mut bindings::led_classdev) -> &'a Self { > > This isn't a bug, but functions that are small or forwarding to a binding > call should be annotated with #[inline]. > > [ ... ] > > > + /// Returns the subleds passed to [`Device::new_multicolor`]. > > + #[inline] > > + pub fn subleds(&self) -> &[MultiColorSubLed] { > > + // SAFETY: The existence of `self` guarantees that `self.classdev.get()` is a pointer to a > > + // valid `led_classdev_mc`. > > + let raw = unsafe { &*self.classdev.get() }; > > + // SAFETY: `raw.subled_info` is a valid pointer to `mc_subled[num_colors]`. > > + // CAST: The safeguards in the const block ensure that `MultiColorSubLed` has an identical > > + // layout to `mc_subled`. > > + unsafe { > > + core::slice::from_raw_parts( > > + raw.subled_info.cast::<MultiColorSubLed>(), > > + // CAST: It is guaranteed that `num_colors` fits into an `usize`. > > + raw.num_colors as usize, > > + ) > > + } > > + } > > Does returning a shared reference &[MultiColorSubLed] violate Rust's > aliasing rules? > > The C LED subsystem can concurrently mutate the brightness and intensity > fields of these subleds (e.g., in led_mc_calc_color_components or during > sysfs writes). > > Mutating memory from C code while a Rust driver holds a shared reference > could result in undefined behavior. Should these mutable fields be wrapped in > an Opaque<u32> to permit interior mutability? > > [ ... ] > > > +#[pinned_drop] > > +impl<T: LedOps<Mode = MultiColor>> PinnedDrop for MultiColorDevice<T> { > > + fn drop(self: Pin<&mut Self>) { > [ ... ] > > + // SAFETY: `subled_info` is guaranteed to be a valid array pointer to `mc_subled` with the > > + // length and capacity of `led_cdev.num_colors`. See `led::MulticolorDevice::new`. > > + drop(unsafe { > > + KVec::from_raw_parts( > > + led_cdev.subled_info, > > + led_cdev.num_colors as usize, > > + led_cdev.num_colors as usize, > > + ) > > + }); > > + } > > +} > > Similar to the error path above, could reconstructing this vector with > led_cdev.num_colors as the capacity cause memory corruption if the original > allocation capacity was larger?
signature.asc
(application/pgp-signature, 870 B)
-----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEgnQYxPSsWOdyMMRzNHYf+OetQ9IFAmn0z+QbFIAAAAAABAAO bWFudTIsMi41KzEuMTIsMiwyAAoJEDR2H/jnrUPSWUwQAI/ZRGNalqCwSZeD+/zT dpnsS+LMW5LPNo5FMctpBpXfwTBa3uGtjYirO2tiR/jdRf4Pn6UcA53MdJgGaeAy Sf8zsYUUbIuIu0CiBHzsDJsvxwEPOVvq6jlh9SlOoWvofkUgqOWpkQa9aLtFeewX SA0qZifkYj0CAACgvOT+f/aaszkzvvGBud9vtn3PLW7OQP+dV3UN4EWwtfc1+dol ZwC0wcJZtzwncjKnD3TcXoEE9pvVUsZNbYI0FndtJmGmpQ/yYPmgb70NvSPeNeNq uMKjysI1cBgrdXP3Y/VHcQ4dfiDyPC7WicEpgDQVFevHJYrXVVuLXVLqZYb+M86l zh3QgAps/PHnfkfTZj3CkXlNo/J0/ZzpqxDk1OtrUDdA6p7Q/ceeycBctAvo5Kd+ YT/ztfwryj8tdlF5gJNmpKnJT5b+K+WA9LcmpisLmYyQYYdg6rI6S1jZWWYQ2tMq hCp4T3yOrjZCf0Kl5lX6o6ChQqPenmnHvetPLsgZ1exZ2UfbbVhXUEtGLsM1Bnas aFr9ZcRxCR4fc+prvPMo9eGjcbSpb3DY5eQo6YcLP2d+BRKRjvAqCbgWSB3B0icx R3ujFxMf/LZ+l6RjSbulXGNyJlpphfvZf+AqIpINd0lpAaOhTJ3Iwvkdrcw2nglO aS+IVhP9W32tZ6sWlW3rCxzM =H468 -----END PGP SIGNATURE-----