You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The idea is a #[repr(transparent)] wrapper over [T] with a checked constructor, starting with only what strcpy_into_buf() in uaccess needs to drop its unwrap_unchecked(). Other users like net/phy.rs can follow with the methods they need.
I'd generalize this to a NonEmpty wrapper that can wrap all types of containers (and deref to its content). It would invoke a IsEmpty trait to determine if something is empty.
From d0e2557f69f88affb5b03bee26a8bec5ac8215d6 Mon Sep 17 00:00:00 2001
From: Francis Laniel <laniel_francis@npnm.eu>
Date: Sun, 11 Oct 2026 21:40:15 +0300
Subject: [RFC PATCH v1 1/2] rust: add NonEmptySlice
Add NonEmptySlice<T>, a [T] wrapper that is guaranteed to contain at
least one element.
This lets methods such as last_mut() return a plain reference instead
of an Option, so callers that have already checked for emptiness no
longer need unwrap() or unwrap_unchecked().
Suggested-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Francis Laniel <laniel_francis@npnm.eu>
---
rust/kernel/lib.rs | 1 +
rust/kernel/slice.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+)
create mode 100644 rust/kernel/slice.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 7225abc64084..8cc01aceb3b2 100644
--- a/rust/kernel/lib.rs+++ b/rust/kernel/lib.rs@@ -130,6 +130,7 @@
#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
pub mod serdev;
pub mod sizes;
+pub mod slice;
#[cfg(CONFIG_SOC_BUS)]
pub mod soc;
#[doc(hidden)]
diff --git a/rust/kernel/slice.rs b/rust/kernel/slice.rs
new file mode 100644
index 000000000000..7e467cb952c3
--- /dev/null+++ b/rust/kernel/slice.rs@@ -0,0 +1,76 @@+// SPDX-License-Identifier: GPL-2.0++//! Slice abstractions to ease development.++use core::ops::{Deref, DerefMut};++/// A slice that is guaranteed to contain at least one element.+///+/// # Invariants+///+/// `self.0` is not empty.+#[repr(transparent)]+pub struct NonEmptySlice<T>([T]);++impl<T> NonEmptySlice<T> {+ /// Returns `None` if `s` is empty.+ pub fn new(s: &mut [T]) -> Option<&Self> {+ if s.is_empty() {+ return None;+ }+ // SAFETY: `Self` is `repr(transparent)` over `[T]`, so the cast keeps the slice length.+ // INVARIANT: `s` is not empty, checked above.+ Some(unsafe { &*(core::ptr::from_mut(s) as *const Self) })+ }++ /// Returns `None` if `s` is empty.+ pub fn new_mut(s: &mut [T]) -> Option<&mut Self> {+ if s.is_empty() {+ return None;+ }+ // SAFETY: `Self` is `repr(transparent)` over `[T]`, so the cast keeps the slice length.+ // INVARIANT: `s` is not empty, checked above.+ Some(unsafe { &mut *(core::ptr::from_mut(s) as *mut Self) })+ }++ /// Returns a reference to the first element.+ pub fn first(&mut self) -> &T {+ // SAFETY: By the type invariant, `len >= 1`, so `0` is in bounds.+ unsafe { self.0.get_unchecked(0) }+ }++ /// Returns a mutable reference to the first element.+ pub fn first_mut(&mut self) -> &mut T {+ // SAFETY: By the type invariant, `len >= 1`, so `0` is in bounds.+ unsafe { self.0.get_unchecked_mut(0) }+ }++ /// Returns a reference to the last element.+ pub fn last(&mut self) -> &T {+ let last = self.0.len() - 1;+ // SAFETY: By the type invariant, `len >= 1`, so `last` is in bounds.+ unsafe { self.0.get_unchecked(last) }+ }++ /// Returns a mutable reference to the last element.+ pub fn last_mut(&mut self) -> &mut T {+ let last = self.0.len() - 1;+ // SAFETY: By the type invariant, `len >= 1`, so `last` is in bounds.+ unsafe { self.0.get_unchecked_mut(last) }+ }+}++impl<T> Deref for NonEmptySlice<T> {+ type Target = [T];++ fn deref(&self) -> &[T] {+ &self.0+ }+}++impl<T> DerefMut for NonEmptySlice<T> {+ // Mutating elements through `&mut [T]` can't change the length, so the invariant holds.+ fn deref_mut(&mut self) -> &mut [T] {+ &mut self.0+ }+}--
2.47.3
From eae77376e0bccfcd779113b9eedf17f481481d24 Mon Sep 17 00:00:00 2001
From: Francis Laniel <laniel_francis@npnm.eu>
Date: Sun, 11 Oct 2026 21:42:34 +0300
Subject: [RFC PATCH v1 2/2] rust: uaccess: Use slice::NonEmptySlice in
strcpy_into_buf()
This removes an unsafe block in strcpy_into_buf(), the unsafe
block now lies in last_mut() in NonEmptySlice.
Signed-off-by: Francis Laniel <laniel_francis@npnm.eu>
Suggested-by: Gary Guo <gary@garyguo.net>
---
rust/kernel/uaccess.rs | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index 5f6c4d7a1a51..b6bf9074d67a 100644
--- a/rust/kernel/uaccess.rs+++ b/rust/kernel/uaccess.rs@@ -13,6 +13,7 @@
fs::file,
prelude::*,
ptr::KnownSize,
+ slice::NonEmptySlice,
transmute::{AsBytes, FromBytes},
};
use core::mem::{size_of, MaybeUninit};
@@ -390,13 +391,11 @@ pub fn read_all<A: Allocator>(mut self, buf: &mut Vec<u8, A>, flags: Flags) -> R
/// copied).
#[doc(alias = "strncpy_from_user")]
pub fn strcpy_into_buf<'buf>(self, buf: &'buf mut [u8]) -> Result<&'buf CStr> {
- if buf.is_empty() {- return Err(EINVAL);- }+ let buf = NonEmptySlice::new_mut(buf).ok_or(EINVAL)?;
// SAFETY: The types are compatible and `strncpy_from_user` doesn't write uninitialized
// bytes to `buf`.
- let mut dst = unsafe { &mut *(core::ptr::from_mut(buf) as *mut [MaybeUninit<u8>]) };+ let mut dst = unsafe { &mut *(core::ptr::from_mut(&mut **buf) as *mut [MaybeUninit<u8>]) };
// We never read more than `self.length` bytes.
if dst.len() > self.length {
@@ -422,9 +421,7 @@ pub fn strcpy_into_buf<'buf>(self, buf: &'buf mut [u8]) -> Result<&'buf CStr> {
// This means that we filled the buffer exactly. In this case, we add a NUL-terminator
// and return it. Unlike the `len < dst.len()` branch, don't modify `len` because it
// already represents the length including the NUL-terminator.
- //- // SAFETY: Due to the check at the beginning, the buffer is not empty.- unsafe { *buf.last_mut().unwrap_unchecked() = 0 };+ *buf.last_mut() = 0;
}
// This method consumes `self`, so it can only be called once, thus we do not need to
--
2.47.3
I am not a rust native speaker, if you see any ways to improve it, feel free to share it or to send directly your upgraded version of the patch 😄.
I'd generalize this to a NonEmpty wrapper that can wrap all types of containers (and deref to its content). It would invoke a IsEmpty trait to determine if something is empty.
@nbdd0121 Makes sense, I've reworked it that way and I'm finishing up v1 now.
@eiffel-fl Thanks for reaching out on Zulip. I'll Cc you on the series; a review would be welcome.
Activity
LNSD commentedon Oct 11, 2026
I'd like to take this one.
The idea is a
#[repr(transparent)]wrapper over[T]with a checked constructor, starting with only whatstrcpy_into_buf()in uaccess needs to drop itsunwrap_unchecked(). Other users likenet/phy.rscan follow with the methods they need.nbdd0121 commentedon Oct 11, 2026
I'd generalize this to a
NonEmptywrapper that can wrap all types of containers (and deref to its content). It would invoke aIsEmptytrait to determine if something is empty.eiffel-fl commentedon Oct 12, 2026
Folks, I wrote the following and sent it upstream (https://lore.kernel.org/rust-for-linux/DM2FHZGKS6OA.W3GBZJFVVIVY@nvidia.com/T/#t), though maybe a bit too fast:
I am not a rust native speaker, if you see any ways to improve it, feel free to share it or to send directly your upgraded version of the patch 😄.
LNSD commentedon Oct 12, 2026
@nbdd0121 Makes sense, I've reworked it that way and I'm finishing up v1 now.
@eiffel-fl Thanks for reaching out on Zulip. I'll Cc you on the series; a review would be welcome.