rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-)
Methods receive a slice and perform size check to add a valid way to make
conversion safe. An Option is used, in error case just return `None`.
The conversion between slices `[T]` is separated from others, because I
couldn't implement it in the same way as the other conversions.
Link: https://github.com/Rust-for-Linux/linux/issues/1119
Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com>
---
Changes in v2:
- Rollback the implementation for the macro in the repository and implement
methods in trait
- Link to v2: https://lore.kernel.org/rust-for-linux/20241012193657.290cc79c@eugeo/T/#t
Changes in v3:
- Fix grammar errors
- Remove repeated tests
- Fix alignment errors
- Fix tests not building
- Link to v3: https://lore.kernel.org/rust-for-linux/20241109055442.85190-1-christiansantoslima21@gmail.com/
Changes in v4:
- Removed core::simd::ToBytes
- Changed trait and methods to safe Add
- Result<&Self, Error> in order to make safe methods
- Link to v4: https://lore.kernel.org/rust-for-linux/20250314034910.134463-1-christiansantoslima21@gmail.com/
Changes in v5:
- Changed from Result to Option
- Removed commentaries
- Returned trait impl to unsafe
- Link to v5: https://lore.kernel.org/rust-for-linux/20250320014041.101470-1-christiansantoslima21@gmail.com/
Changes in v6:
- Add endianess check to doc test and use match to check
success case
- Reformulated safety comments
---
rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++---
1 file changed, 83 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
index 1c7d43771a37..16dfa5c7d467 100644
--- a/rust/kernel/transmute.rs
+++ b/rust/kernel/transmute.rs
@@ -9,29 +9,106 @@
///
/// It's okay for the type to have padding, as initializing those bytes has no effect.
///
+/// # Example
+/// ```
+/// let foo = &[1, 2, 3, 4];
+///
+/// let result = u32::from_bytes(foo);
+///
+/// #[cfg(target_endian = "little")]
+/// match result {
+/// Some(x) => assert_eq!(*x, 0x4030201),
+/// None => unreachable!()
+/// }
+///
+/// #[cfg(target_endian = "big")]
+/// match result {
+/// Some(x) => assert_eq!(*x, 0x1020304),
+/// None => unreachable!()
+/// }
+/// ```
+///
/// # Safety
///
/// All bit-patterns must be valid for this type. This type must not have interior mutability.
-pub unsafe trait FromBytes {}
+pub unsafe trait FromBytes {
+ /// Converts a slice of bytes to a reference to `Self` when possible.
+ fn from_bytes(bytes: &[u8]) -> Option<&Self>;
+
+ /// Converts a mutable slice of bytes to a reference to `Self` when possible.
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
+ where
+ Self: AsBytes;
+}
macro_rules! impl_frombytes {
($($({$($generics:tt)*})? $t:ty, )*) => {
// SAFETY: Safety comments written in the macro invocation.
- $(unsafe impl$($($generics)*)? FromBytes for $t {})*
+ $(unsafe impl$($($generics)*)? FromBytes for $t {
+ fn from_bytes(bytes: &[u8]) -> Option<&$t> {
+ if bytes.len() == core::mem::size_of::<$t>() {
+ let slice_ptr = bytes.as_ptr().cast::<$t>();
+ unsafe { Some(&*slice_ptr) }
+ } else {
+ None
+ }
+ }
+
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut $t>
+ where
+ Self: AsBytes,
+ {
+ if bytes.len() == core::mem::size_of::<$t>() {
+ let slice_ptr = bytes.as_mut_ptr().cast::<$t>();
+ unsafe { Some(&mut *slice_ptr) }
+ } else {
+ None
+ }
+ }
+ })*
};
}
impl_frombytes! {
// SAFETY: All bit patterns are acceptable values of the types below.
+ // Checking the pointer size makes this operation safe and it's necessary
+ // to dereference to get the value and return it as a reference to `Self`.
u8, u16, u32, u64, usize,
i8, i16, i32, i64, isize,
-
- // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
- // patterns are also acceptable for arrays of that type.
- {<T: FromBytes>} [T],
{<T: FromBytes, const N: usize>} [T; N],
}
+// SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
+// patterns are also acceptable for arrays of that type.
+unsafe impl<T: FromBytes> FromBytes for [T] {
+ fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+ let slice_ptr = bytes.as_ptr().cast::<T>();
+ if bytes.len() % core::mem::size_of::<T>() == 0 {
+ let slice_len = bytes.len() / core::mem::size_of::<T>();
+ // SAFETY: Since the code checks the size and can be divided into blocks of size T
+ // the slice is valid because the size is multiple of T.
+ unsafe { Some(core::slice::from_raw_parts(slice_ptr, slice_len)) }
+ } else {
+ None
+ }
+ }
+
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
+ where
+ Self: AsBytes,
+ {
+ let slice_ptr = bytes.as_mut_ptr().cast::<T>();
+ if bytes.len() % core::mem::size_of::<T>() == 0 {
+ let slice_len = bytes.len() / core::mem::size_of::<T>();
+ // SAFETY: Since the code checks the size and can be divided into blocks of size T
+ // the slice is valid because the size is multiple of T.
+ unsafe { Some(core::slice::from_raw_parts_mut(slice_ptr, slice_len)) }
+ } else {
+ None
+ }
+ }
+}
+
/// Types that can be viewed as an immutable slice of initialized bytes.
///
/// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This
--
2.49.0
Hi Christian, On Sun, Mar 30, 2025 at 08:40:15PM -0300, Christian S. Lima wrote: > Methods receive a slice and perform size check to add a valid way to make > conversion safe. An Option is used, in error case just return `None`. > > The conversion between slices `[T]` is separated from others, because I > couldn't implement it in the same way as the other conversions. > > Link: https://github.com/Rust-for-Linux/linux/issues/1119 > Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com> What's the status of the series? Are you still working on this topic? - Danilo
> What's the status of the series? Are you still working on this topic Hi Danilo, I'm currently working on the alignment issue that was mentioned in that email. I'm still working on it, and I'll send a patch this week.
On Mon Mar 31, 2025 at 8:40 AM JST, Christian S. Lima wrote:
> Methods receive a slice and perform size check to add a valid way to make
> conversion safe. An Option is used, in error case just return `None`.
>
> The conversion between slices `[T]` is separated from others, because I
> couldn't implement it in the same way as the other conversions.
>
> Link: https://github.com/Rust-for-Linux/linux/issues/1119
> Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com>
> ---
> Changes in v2:
> - Rollback the implementation for the macro in the repository and implement
> methods in trait
> - Link to v2: https://lore.kernel.org/rust-for-linux/20241012193657.290cc79c@eugeo/T/#t
>
> Changes in v3:
> - Fix grammar errors
> - Remove repeated tests
> - Fix alignment errors
> - Fix tests not building
> - Link to v3: https://lore.kernel.org/rust-for-linux/20241109055442.85190-1-christiansantoslima21@gmail.com/
>
> Changes in v4:
> - Removed core::simd::ToBytes
> - Changed trait and methods to safe Add
> - Result<&Self, Error> in order to make safe methods
> - Link to v4: https://lore.kernel.org/rust-for-linux/20250314034910.134463-1-christiansantoslima21@gmail.com/
>
> Changes in v5:
> - Changed from Result to Option
> - Removed commentaries
> - Returned trait impl to unsafe
> - Link to v5: https://lore.kernel.org/rust-for-linux/20250320014041.101470-1-christiansantoslima21@gmail.com/
>
> Changes in v6:
> - Add endianess check to doc test and use match to check
> success case
> - Reformulated safety comments
> ---
> rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++---
> 1 file changed, 83 insertions(+), 6 deletions(-)
>
> diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
> index 1c7d43771a37..16dfa5c7d467 100644
> --- a/rust/kernel/transmute.rs
> +++ b/rust/kernel/transmute.rs
> @@ -9,29 +9,106 @@
> ///
> /// It's okay for the type to have padding, as initializing those bytes has no effect.
> ///
> +/// # Example
> +/// ```
> +/// let foo = &[1, 2, 3, 4];
> +///
> +/// let result = u32::from_bytes(foo);
> +///
> +/// #[cfg(target_endian = "little")]
> +/// match result {
> +/// Some(x) => assert_eq!(*x, 0x4030201),
> +/// None => unreachable!()
> +/// }
> +///
> +/// #[cfg(target_endian = "big")]
> +/// match result {
> +/// Some(x) => assert_eq!(*x, 0x1020304),
> +/// None => unreachable!()
> +/// }
> +/// ```
> +///
> /// # Safety
> ///
> /// All bit-patterns must be valid for this type. This type must not have interior mutability.
> -pub unsafe trait FromBytes {}
> +pub unsafe trait FromBytes {
> + /// Converts a slice of bytes to a reference to `Self` when possible.
> + fn from_bytes(bytes: &[u8]) -> Option<&Self>;
> +
> + /// Converts a mutable slice of bytes to a reference to `Self` when possible.
> + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
> + where
> + Self: AsBytes;
> +}
>
> macro_rules! impl_frombytes {
> ($($({$($generics:tt)*})? $t:ty, )*) => {
> // SAFETY: Safety comments written in the macro invocation.
> - $(unsafe impl$($($generics)*)? FromBytes for $t {})*
> + $(unsafe impl$($($generics)*)? FromBytes for $t {
> + fn from_bytes(bytes: &[u8]) -> Option<&$t> {
> + if bytes.len() == core::mem::size_of::<$t>() {
> + let slice_ptr = bytes.as_ptr().cast::<$t>();
> + unsafe { Some(&*slice_ptr) }
> + } else {
> + None
> + }
> + }
This is probably a naive question, but is there a reason why this cannot
be the default implementation of `from_bytes`? IIUC other implementors
would have to come with their own implementation, which adds an extra
burden.
On Sun, Mar 30, 2025 at 4:40 PM Christian S. Lima
<christiansantoslima21@gmail.com> wrote:
>
> Methods receive a slice and perform size check to add a valid way to make
> conversion safe. An Option is used, in error case just return `None`.
>
> The conversion between slices `[T]` is separated from others, because I
> couldn't implement it in the same way as the other conversions.
>
> Link: https://github.com/Rust-for-Linux/linux/issues/1119
> Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com>
> ---
> Changes in v2:
> - Rollback the implementation for the macro in the repository and implement
> methods in trait
> - Link to v2: https://lore.kernel.org/rust-for-linux/20241012193657.290cc79c@eugeo/T/#t
>
> Changes in v3:
> - Fix grammar errors
> - Remove repeated tests
> - Fix alignment errors
> - Fix tests not building
> - Link to v3: https://lore.kernel.org/rust-for-linux/20241109055442.85190-1-christiansantoslima21@gmail.com/
>
> Changes in v4:
> - Removed core::simd::ToBytes
> - Changed trait and methods to safe Add
> - Result<&Self, Error> in order to make safe methods
> - Link to v4: https://lore.kernel.org/rust-for-linux/20250314034910.134463-1-christiansantoslima21@gmail.com/
>
> Changes in v5:
> - Changed from Result to Option
> - Removed commentaries
> - Returned trait impl to unsafe
> - Link to v5: https://lore.kernel.org/rust-for-linux/20250320014041.101470-1-christiansantoslima21@gmail.com/
>
> Changes in v6:
> - Add endianess check to doc test and use match to check
> success case
> - Reformulated safety comments
> ---
> rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++---
> 1 file changed, 83 insertions(+), 6 deletions(-)
>
> diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
> index 1c7d43771a37..16dfa5c7d467 100644
> --- a/rust/kernel/transmute.rs
> +++ b/rust/kernel/transmute.rs
> @@ -9,29 +9,106 @@
> ///
> /// It's okay for the type to have padding, as initializing those bytes has no effect.
> ///
> +/// # Example
> +/// ```
> +/// let foo = &[1, 2, 3, 4];
> +///
> +/// let result = u32::from_bytes(foo);
> +///
> +/// #[cfg(target_endian = "little")]
> +/// match result {
> +/// Some(x) => assert_eq!(*x, 0x4030201),
> +/// None => unreachable!()
> +/// }
> +///
> +/// #[cfg(target_endian = "big")]
> +/// match result {
> +/// Some(x) => assert_eq!(*x, 0x1020304),
> +/// None => unreachable!()
> +/// }
> +/// ```
> +///
> /// # Safety
> ///
> /// All bit-patterns must be valid for this type. This type must not have interior mutability.
> -pub unsafe trait FromBytes {}
> +pub unsafe trait FromBytes {
> + /// Converts a slice of bytes to a reference to `Self` when possible.
> + fn from_bytes(bytes: &[u8]) -> Option<&Self>;
> +
> + /// Converts a mutable slice of bytes to a reference to `Self` when possible.
> + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
> + where
> + Self: AsBytes;
> +}
>
> macro_rules! impl_frombytes {
> ($($({$($generics:tt)*})? $t:ty, )*) => {
> // SAFETY: Safety comments written in the macro invocation.
> - $(unsafe impl$($($generics)*)? FromBytes for $t {})*
> + $(unsafe impl$($($generics)*)? FromBytes for $t {
> + fn from_bytes(bytes: &[u8]) -> Option<&$t> {
Consider factoring this out into a helper function, e.g.
```
fn from_bytes_sized<T: FromBytes + Sized>(bytes: &[u8]) -> Option<&T> {
```
which you can then call in here. If you were not trying to handle
`?Sized`, we could even put it in the trait default implementation.
> + if bytes.len() == core::mem::size_of::<$t>() {
> + let slice_ptr = bytes.as_ptr().cast::<$t>();
There's no alignment check, and so the resulting constructed reference
may be misaligned, which is UB. Same below.
> + unsafe { Some(&*slice_ptr) }
> + } else {
> + None
> + }
> + }
> +
> + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut $t>
> + where
> + Self: AsBytes,
> + {
> + if bytes.len() == core::mem::size_of::<$t>() {
> + let slice_ptr = bytes.as_mut_ptr().cast::<$t>();
> + unsafe { Some(&mut *slice_ptr) }
> + } else {
> + None
> + }
> + }
> + })*
> };
> }
>
> impl_frombytes! {
> // SAFETY: All bit patterns are acceptable values of the types below.
> + // Checking the pointer size makes this operation safe and it's necessary
> + // to dereference to get the value and return it as a reference to `Self`.
> u8, u16, u32, u64, usize,
> i8, i16, i32, i64, isize,
> -
> - // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
> - // patterns are also acceptable for arrays of that type.
> - {<T: FromBytes>} [T],
> {<T: FromBytes, const N: usize>} [T; N],
> }
>
> +// SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
> +// patterns are also acceptable for arrays of that type.
> +unsafe impl<T: FromBytes> FromBytes for [T] {
> + fn from_bytes(bytes: &[u8]) -> Option<&Self> {
> + let slice_ptr = bytes.as_ptr().cast::<T>();
> + if bytes.len() % core::mem::size_of::<T>() == 0 {
> + let slice_len = bytes.len() / core::mem::size_of::<T>();
> + // SAFETY: Since the code checks the size and can be divided into blocks of size T
> + // the slice is valid because the size is multiple of T.
> + unsafe { Some(core::slice::from_raw_parts(slice_ptr, slice_len)) }
> + } else {
> + None
> + }
> + }
> +
> + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
> + where
> + Self: AsBytes,
> + {
> + let slice_ptr = bytes.as_mut_ptr().cast::<T>();
> + if bytes.len() % core::mem::size_of::<T>() == 0 {
> + let slice_len = bytes.len() / core::mem::size_of::<T>();
> + // SAFETY: Since the code checks the size and can be divided into blocks of size T
> + // the slice is valid because the size is multiple of T.
> + unsafe { Some(core::slice::from_raw_parts_mut(slice_ptr, slice_len)) }
> + } else {
> + None
> + }
> + }
> +}
> +
> /// Types that can be viewed as an immutable slice of initialized bytes.
> ///
> /// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This
> --
> 2.49.0
>
>
> Consider factoring this out into a helper function, e.g.
> ```
> fn from_bytes_sized<T: FromBytes + Sized>(bytes: &[u8]) -> Option<&T> {
> ```
> which you can then call in here. If you were not trying to handle
> `?Sized`, we could even put it in the trait default implementation.
What do you think about
```
fn from_bytes(bytes: &[u8]) -> Option<&Self>
where:
Self: Sized
```
for the default implementation?
> > + if bytes.len() == core::mem::size_of::<$t>() {
> > + let slice_ptr = bytes.as_ptr().cast::<$t>();
>
> There's no alignment check, and so the resulting constructed reference
> may be misaligned, which is UB. Same below.
I see, I didn't think about it. Good catch and thanks for the tip!
On Wed, Apr 9, 2025 at 3:32 PM Matthew Maurer <mmaurer@google.com> wrote:
>
> On Sun, Mar 30, 2025 at 4:40 PM Christian S. Lima
> <christiansantoslima21@gmail.com> wrote:
> >
> > Methods receive a slice and perform size check to add a valid way to make
> > conversion safe. An Option is used, in error case just return `None`.
> >
> > The conversion between slices `[T]` is separated from others, because I
> > couldn't implement it in the same way as the other conversions.
> >
> > Link: https://github.com/Rust-for-Linux/linux/issues/1119
> > Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com>
> > ---
> > Changes in v2:
> > - Rollback the implementation for the macro in the repository and implement
> > methods in trait
> > - Link to v2: https://lore.kernel.org/rust-for-linux/20241012193657.290cc79c@eugeo/T/#t
> >
> > Changes in v3:
> > - Fix grammar errors
> > - Remove repeated tests
> > - Fix alignment errors
> > - Fix tests not building
> > - Link to v3: https://lore.kernel.org/rust-for-linux/20241109055442.85190-1-christiansantoslima21@gmail.com/
> >
> > Changes in v4:
> > - Removed core::simd::ToBytes
> > - Changed trait and methods to safe Add
> > - Result<&Self, Error> in order to make safe methods
> > - Link to v4: https://lore.kernel.org/rust-for-linux/20250314034910.134463-1-christiansantoslima21@gmail.com/
> >
> > Changes in v5:
> > - Changed from Result to Option
> > - Removed commentaries
> > - Returned trait impl to unsafe
> > - Link to v5: https://lore.kernel.org/rust-for-linux/20250320014041.101470-1-christiansantoslima21@gmail.com/
> >
> > Changes in v6:
> > - Add endianess check to doc test and use match to check
> > success case
> > - Reformulated safety comments
> > ---
> > rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++---
> > 1 file changed, 83 insertions(+), 6 deletions(-)
> >
> > diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
> > index 1c7d43771a37..16dfa5c7d467 100644
> > --- a/rust/kernel/transmute.rs
> > +++ b/rust/kernel/transmute.rs
> > @@ -9,29 +9,106 @@
> > ///
> > /// It's okay for the type to have padding, as initializing those bytes has no effect.
> > ///
> > +/// # Example
> > +/// ```
> > +/// let foo = &[1, 2, 3, 4];
> > +///
> > +/// let result = u32::from_bytes(foo);
> > +///
> > +/// #[cfg(target_endian = "little")]
> > +/// match result {
> > +/// Some(x) => assert_eq!(*x, 0x4030201),
> > +/// None => unreachable!()
> > +/// }
> > +///
> > +/// #[cfg(target_endian = "big")]
> > +/// match result {
> > +/// Some(x) => assert_eq!(*x, 0x1020304),
> > +/// None => unreachable!()
> > +/// }
> > +/// ```
> > +///
> > /// # Safety
> > ///
> > /// All bit-patterns must be valid for this type. This type must not have interior mutability.
> > -pub unsafe trait FromBytes {}
> > +pub unsafe trait FromBytes {
> > + /// Converts a slice of bytes to a reference to `Self` when possible.
> > + fn from_bytes(bytes: &[u8]) -> Option<&Self>;
> > +
> > + /// Converts a mutable slice of bytes to a reference to `Self` when possible.
> > + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
> > + where
> > + Self: AsBytes;
> > +}
> >
> > macro_rules! impl_frombytes {
> > ($($({$($generics:tt)*})? $t:ty, )*) => {
> > // SAFETY: Safety comments written in the macro invocation.
> > - $(unsafe impl$($($generics)*)? FromBytes for $t {})*
> > + $(unsafe impl$($($generics)*)? FromBytes for $t {
> > + fn from_bytes(bytes: &[u8]) -> Option<&$t> {
>
> Consider factoring this out into a helper function, e.g.
> ```
> fn from_bytes_sized<T: FromBytes + Sized>(bytes: &[u8]) -> Option<&T> {
> ```
> which you can then call in here. If you were not trying to handle
> `?Sized`, we could even put it in the trait default implementation.
>
> > + if bytes.len() == core::mem::size_of::<$t>() {
> > + let slice_ptr = bytes.as_ptr().cast::<$t>();
>
> There's no alignment check, and so the resulting constructed reference
> may be misaligned, which is UB. Same below.
>
> > + unsafe { Some(&*slice_ptr) }
> > + } else {
> > + None
> > + }
> > + }
> > +
> > + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut $t>
Sorry, one last thing - could we consider `from_mut_bytes` instead of
`from_bytes_mut` to match the current API of `zerocopy`?
> > + where
> > + Self: AsBytes,
> > + {
> > + if bytes.len() == core::mem::size_of::<$t>() {
> > + let slice_ptr = bytes.as_mut_ptr().cast::<$t>();
> > + unsafe { Some(&mut *slice_ptr) }
> > + } else {
> > + None
> > + }
> > + }
> > + })*
> > };
> > }
> >
> > impl_frombytes! {
> > // SAFETY: All bit patterns are acceptable values of the types below.
> > + // Checking the pointer size makes this operation safe and it's necessary
> > + // to dereference to get the value and return it as a reference to `Self`.
> > u8, u16, u32, u64, usize,
> > i8, i16, i32, i64, isize,
> > -
> > - // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
> > - // patterns are also acceptable for arrays of that type.
> > - {<T: FromBytes>} [T],
> > {<T: FromBytes, const N: usize>} [T; N],
> > }
> >
> > +// SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
> > +// patterns are also acceptable for arrays of that type.
> > +unsafe impl<T: FromBytes> FromBytes for [T] {
> > + fn from_bytes(bytes: &[u8]) -> Option<&Self> {
> > + let slice_ptr = bytes.as_ptr().cast::<T>();
> > + if bytes.len() % core::mem::size_of::<T>() == 0 {
> > + let slice_len = bytes.len() / core::mem::size_of::<T>();
> > + // SAFETY: Since the code checks the size and can be divided into blocks of size T
> > + // the slice is valid because the size is multiple of T.
> > + unsafe { Some(core::slice::from_raw_parts(slice_ptr, slice_len)) }
> > + } else {
> > + None
> > + }
> > + }
> > +
> > + fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
> > + where
> > + Self: AsBytes,
> > + {
> > + let slice_ptr = bytes.as_mut_ptr().cast::<T>();
> > + if bytes.len() % core::mem::size_of::<T>() == 0 {
> > + let slice_len = bytes.len() / core::mem::size_of::<T>();
> > + // SAFETY: Since the code checks the size and can be divided into blocks of size T
> > + // the slice is valid because the size is multiple of T.
> > + unsafe { Some(core::slice::from_raw_parts_mut(slice_ptr, slice_len)) }
> > + } else {
> > + None
> > + }
> > + }
> > +}
> > +
> > /// Types that can be viewed as an immutable slice of initialized bytes.
> > ///
> > /// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This
> > --
> > 2.49.0
> >
> >
Methods receive a slice and perform size check to add a valid way to make
conversion safe. An Option is used, in error case just return `None`.
The conversion between slices `[T]` is separated from others, because I
couldn't implement it in the same way as the other conversions.
Link: https://github.com/Rust-for-Linux/linux/issues/1119
Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com>
---
Changes in v2:
- Rollback the implementation for the macro in the repository and implement
methods in trait
- Link to v2: https://lore.kernel.org/rust-for-linux/20241012193657.290cc79c@eugeo/T/#t
Changes in v3:
- Fix grammar errors
- Remove repeated tests
- Fix alignment errors
- Fix tests not building
- Link to v3: https://lore.kernel.org/rust-for-linux/20241109055442.85190-1-christiansantoslima21@gmail.com/
Changes in v4:
- Removed core::simd::ToBytes
- Changed trait and methods to safe Add
- Result<&Self, Error> in order to make safe methods
- Link to v4: https://lore.kernel.org/rust-for-linux/20250314034910.134463-1-christiansantoslima21@gmail.com/
Changes in v5:
- Changed from Result to Option
- Removed commentaries
- Returned trait impl to unsafe
- Link to v5: https://lore.kernel.org/rust-for-linux/20250320014041.101470-1-christiansantoslima21@gmail.com/
Changes in v6:
- Add endianess check to doc test and use match to check
success case
- Reformulated safety comments
---
rust/kernel/transmute.rs | 89 +++++++++++++++++++++++++++++++++++++---
1 file changed, 83 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
index 1c7d43771a37..16dfa5c7d467 100644
--- a/rust/kernel/transmute.rs
+++ b/rust/kernel/transmute.rs
@@ -9,29 +9,106 @@
///
/// It's okay for the type to have padding, as initializing those bytes has no effect.
///
+/// # Example
+/// ```
+/// let foo = &[1, 2, 3, 4];
+///
+/// let result = u32::from_bytes(foo);
+///
+/// #[cfg(target_endian = "little")]
+/// match result {
+/// Some(x) => assert_eq!(*x, 0x4030201),
+/// None => unreachable!()
+/// }
+///
+/// #[cfg(target_endian = "big")]
+/// match result {
+/// Some(x) => assert_eq!(*x, 0x1020304),
+/// None => unreachable!()
+/// }
+/// ```
+///
/// # Safety
///
/// All bit-patterns must be valid for this type. This type must not have interior mutability.
-pub unsafe trait FromBytes {}
+pub unsafe trait FromBytes {
+ /// Converts a slice of bytes to a reference to `Self` when possible.
+ fn from_bytes(bytes: &[u8]) -> Option<&Self>;
+
+ /// Converts a mutable slice of bytes to a reference to `Self` when possible.
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
+ where
+ Self: AsBytes;
+}
macro_rules! impl_frombytes {
($($({$($generics:tt)*})? $t:ty, )*) => {
// SAFETY: Safety comments written in the macro invocation.
- $(unsafe impl$($($generics)*)? FromBytes for $t {})*
+ $(unsafe impl$($($generics)*)? FromBytes for $t {
+ fn from_bytes(bytes: &[u8]) -> Option<&$t> {
+ if bytes.len() == core::mem::size_of::<$t>() {
+ let slice_ptr = bytes.as_ptr().cast::<$t>();
+ unsafe { Some(&*slice_ptr) }
+ } else {
+ None
+ }
+ }
+
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut $t>
+ where
+ Self: AsBytes,
+ {
+ if bytes.len() == core::mem::size_of::<$t>() {
+ let slice_ptr = bytes.as_mut_ptr().cast::<$t>();
+ unsafe { Some(&mut *slice_ptr) }
+ } else {
+ None
+ }
+ }
+ })*
};
}
impl_frombytes! {
// SAFETY: All bit patterns are acceptable values of the types below.
+ // Checking the pointer size makes this operation safe and it's necessary
+ // to dereference to get the value and return it as a reference to `Self`.
u8, u16, u32, u64, usize,
i8, i16, i32, i64, isize,
-
- // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
- // patterns are also acceptable for arrays of that type.
- {<T: FromBytes>} [T],
{<T: FromBytes, const N: usize>} [T; N],
}
+// SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
+// patterns are also acceptable for arrays of that type.
+unsafe impl<T: FromBytes> FromBytes for [T] {
+ fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+ let slice_ptr = bytes.as_ptr().cast::<T>();
+ if bytes.len() % core::mem::size_of::<T>() == 0 {
+ let slice_len = bytes.len() / core::mem::size_of::<T>();
+ // SAFETY: Since the code checks the size and can be divided into blocks of size T
+ // the slice is valid because the size is multiple of T.
+ unsafe { Some(core::slice::from_raw_parts(slice_ptr, slice_len)) }
+ } else {
+ None
+ }
+ }
+
+ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
+ where
+ Self: AsBytes,
+ {
+ let slice_ptr = bytes.as_mut_ptr().cast::<T>();
+ if bytes.len() % core::mem::size_of::<T>() == 0 {
+ let slice_len = bytes.len() / core::mem::size_of::<T>();
+ // SAFETY: Since the code checks the size and can be divided into blocks of size T
+ // the slice is valid because the size is multiple of T.
+ unsafe { Some(core::slice::from_raw_parts_mut(slice_ptr, slice_len)) }
+ } else {
+ None
+ }
+ }
+}
+
/// Types that can be viewed as an immutable slice of initialized bytes.
///
/// If a struct implements this trait, then it is okay to copy it byte-for-byte to userspace. This
--
2.49.0
On Mon, Mar 31, 2025 at 1:40 AM Christian S. Lima <christiansantoslima21@gmail.com> wrote: > > Methods receive a slice and perform size check to add a valid way to make > conversion safe. An Option is used, in error case just return `None`. > > The conversion between slices `[T]` is separated from others, because I > couldn't implement it in the same way as the other conversions. > > Link: https://github.com/Rust-for-Linux/linux/issues/1119 > Signed-off-by: Christian S. Lima <christiansantoslima21@gmail.com> Hmm... We got two emails, both with the essentially the same content, 1 second apart, the second one without the v6 in the title. The one I am replying to is what seems to be the duplicate one. Cheers, Miguel
Hi, Miguel. Idk what happened seems the git send-email took the commit and the .patch together. Sorry about that and I forgot to reply all. :( > The one I am replying to is what seems to be the duplicate one. Should I submit another patch because of duplication? Thanks, Christian
On Mon, Mar 31, 2025 at 8:18 PM Christian <christiansantoslima21@gmail.com> wrote: > > Hi, Miguel. Idk what happened seems the git send-email took the commit > and the .patch together. Sorry about that and I forgot to reply all. :( > > Should I submit another patch because of duplication? No worries, there is no need to resend for that reason. Thanks! Cheers, Miguel
© 2016 - 2026 Red Hat, Inc.