Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
rust: init: add Zeroable trait and init::zeroed function
Browse files Browse the repository at this point in the history
Add the `Zeroable` trait which marks types that can be initialized by
writing `0x00` to every byte of the type. Also add the `init::zeroed`
function that creates an initializer for a `Zeroable` type that writes
`0x00` to every byte.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Andreas Hindborg <a.hindborg@samsung.com>
Link: https://lore.kernel.org/r/20230408122429.1103522-12-y86-dev@protonmail.com
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
  • Loading branch information
Benno Lossin authored and ojeda committed Apr 12, 2023
1 parent 6841d45 commit 38cde0b
Showing 1 changed file with 95 additions and 2 deletions.
97 changes: 95 additions & 2 deletions rust/kernel/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,14 @@ use crate::{
};
use alloc::boxed::Box;
use core::{
alloc::AllocError, cell::Cell, convert::Infallible, marker::PhantomData, mem::MaybeUninit,
pin::Pin, ptr,
alloc::AllocError,
cell::Cell,
convert::Infallible,
marker::PhantomData,
mem::MaybeUninit,
num::*,
pin::Pin,
ptr::{self, NonNull},
};

#[doc(hidden)]
Expand Down Expand Up @@ -1323,3 +1329,90 @@ pub unsafe trait PinnedDrop: __internal::HasPinData {
/// automatically.
fn drop(self: Pin<&mut Self>, only_call_from_drop: __internal::OnlyCallFromDrop);
}

/// Marker trait for types that can be initialized by writing just zeroes.
///
/// # Safety
///
/// The bit pattern consisting of only zeroes is a valid bit pattern for this type. In other words,
/// this is not UB:
///
/// ```rust,ignore
/// let val: Self = unsafe { core::mem::zeroed() };
/// ```
pub unsafe trait Zeroable {}

/// Create a new zeroed T.
///
/// The returned initializer will write `0x00` to every byte of the given `slot`.
#[inline]
pub fn zeroed<T: Zeroable>() -> impl Init<T> {
// SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T`
// and because we write all zeroes, the memory is initialized.
unsafe {
init_from_closure(|slot: *mut T| {
slot.write_bytes(0, 1);
Ok(())
})
}
}

macro_rules! impl_zeroable {
($($({$($generics:tt)*})? $t:ty, )*) => {
$(unsafe impl$($($generics)*)? Zeroable for $t {})*
};
}

impl_zeroable! {
// SAFETY: All primitives that are allowed to be zero.
bool,
char,
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
f32, f64,

// SAFETY: These are ZSTs, there is nothing to zero.
{<T: ?Sized>} PhantomData<T>, core::marker::PhantomPinned, Infallible, (),

// SAFETY: Type is allowed to take any value, including all zeros.
{<T>} MaybeUninit<T>,

// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
Option<NonZeroU8>, Option<NonZeroU16>, Option<NonZeroU32>, Option<NonZeroU64>,
Option<NonZeroU128>, Option<NonZeroUsize>,
Option<NonZeroI8>, Option<NonZeroI16>, Option<NonZeroI32>, Option<NonZeroI64>,
Option<NonZeroI128>, Option<NonZeroIsize>,

// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
//
// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant.
{<T: ?Sized>} Option<NonNull<T>>,
{<T: ?Sized>} Option<Box<T>>,

// SAFETY: `null` pointer is valid.
//
// We cannot use `T: ?Sized`, since the VTABLE pointer part of fat pointers is not allowed to be
// null.
//
// When `Pointee` gets stabilized, we could use
// `T: ?Sized where <T as Pointee>::Metadata: Zeroable`
{<T>} *mut T, {<T>} *const T,

// SAFETY: `null` pointer is valid and the metadata part of these fat pointers is allowed to be
// zero.
{<T>} *mut [T], {<T>} *const [T], *mut str, *const str,

// SAFETY: `T` is `Zeroable`.
{<const N: usize, T: Zeroable>} [T; N], {<T: Zeroable>} Wrapping<T>,
}

macro_rules! impl_tuple_zeroable {
($(,)?) => {};
($first:ident, $($t:ident),* $(,)?) => {
// SAFETY: All elements are zeroable and padding can be zero.
unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) {}
impl_tuple_zeroable!($($t),* ,);
}
}

impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J);

0 comments on commit 38cde0b

Please sign in to comment.