diff --git a/lib/osi/src/lib.rs b/lib/osi/src/lib.rs index a7c547c..6b77b8a 100644 --- a/lib/osi/src/lib.rs +++ b/lib/osi/src/lib.rs @@ -37,12 +37,14 @@ #![no_std] -extern crate alloc; -extern crate core; - #[cfg(any(test, feature = "std"))] extern crate std; +// Used by macros via `$crate::{alloc,core}::*`, explicitly part of the public +// API. Usually of little use to code outside of this crate, though. +pub extern crate alloc; +pub extern crate core; + pub mod align; pub mod args; pub mod brand; diff --git a/lib/osi/src/mem.rs b/lib/osi/src/mem.rs index 5d536c2..11e158c 100644 --- a/lib/osi/src/mem.rs +++ b/lib/osi/src/mem.rs @@ -3,6 +3,7 @@ //! This module contains functions to help dealing with direct memory //! manipulation and inspection. +use core::mem::MaybeUninit as Uninit; use core::mem::transmute_copy; #[doc(hidden)] @@ -10,11 +11,11 @@ use core::mem::transmute_copy; macro_rules! crate_mem_typed_offset_of { ($container:ty, $field:ident, $ty:ty $(,)?) => { const { - let v = ::core::mem::MaybeUninit::<$container>::uninit(); + let v = $crate::core::mem::MaybeUninit::<$container>::uninit(); let _: *const $ty = unsafe { &raw const ((*v.as_ptr()).$field) }; - ::core::mem::offset_of!($container, $field) + $crate::core::mem::offset_of!($container, $field) } } } @@ -85,7 +86,7 @@ pub const unsafe fn transmute_copy_uninit(src: &Src) -> Dst { // // SAFETY: Delegated to the caller. unsafe { - let mut dst = core::mem::MaybeUninit::::uninit(); + let mut dst = Uninit::::uninit(); copy_unaligned( src as *const Src, dst.as_mut_ptr() as *mut Src, @@ -114,7 +115,7 @@ pub const unsafe fn transmute_copy_uninit(src: &Src) -> Dst { // This is the slow-path that manually iterates the individual bytes, but works // with any data-type, as long as the data-type stays valid with swapped bytes. const unsafe fn bswap_slow(v: &T) -> T { - let mut r = core::mem::MaybeUninit::::uninit(); + let mut r = Uninit::::uninit(); let src = v as *const T as *const u8; let dst = r.as_mut_ptr() as *mut u8; @@ -159,16 +160,38 @@ pub const unsafe fn bswap_copy(v: &T) -> T { } } +/// Alias a type as a [`MaybeUninit`](core::mem::MaybeUninit). +/// +/// Any type can be aliased as a possibly uninitialized type. This is usually +/// only necessary when providing initialized data to code that can handle +/// possibly uninitialized data. +pub const fn as_uninit<'a, T>(v: &'a T) -> &'a Uninit { + unsafe { + core::mem::transmute::<&T, &Uninit>(v) + } +} + +/// Alias a slice as a [`MaybeUninit`](core::mem::MaybeUninit). +/// +/// This works like [`as_uninit()`] but for slices of `T`. +pub const fn slice_as_uninit<'a, T>(v: &'a [T]) -> &'a [Uninit] { + unsafe { + core::mem::transmute::<&[T], &[Uninit]>(v) + } +} + /// Alias a type as a byte slice. /// -/// This function allows accessing any type as a slice of bytes. This is safe -/// for all types. However, the content of padding bytes is neither well -/// defined nor stable. -pub const fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] { - // SAFETY: We retain the allocation size of `T` and its lifetime. Hence, - // the transmute is safe for as long as `'a`. Since `v` is - // borrowed, we prevent mutable access for the entire lifetime of - // the returned value. +/// This function allows accessing any type as a slice of bytes. +/// +/// ## Safety +/// +/// The caller must guarantee that: +/// +/// - `T` has no padding bytes, and all bytes of `v` belong to the type `T. +/// This implies that all bytes in `v` are initialized. +pub const unsafe fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] { + // SAFETY: Propagated to caller. unsafe { core::slice::from_raw_parts::<'a, u8>( v as *const _ as *const _, @@ -185,15 +208,15 @@ pub const fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] { /// /// ## Safety /// -/// Like [`as_bytes()`], this can be safely called on any type. However, unlike -/// [`as_bytes()`], this function grants mutable access and thus any mutable -/// use of the returned reference must guarantee not to violate any invariants -/// of `T`. +/// The caller must guarantee that: +/// +/// - `T` has no padding bytes, and all bytes of `v` belong to the type `T. +/// This implies that all bytes in `v` are initialized. +/// - Any bit sequence in `v` produces a valid value of type `T`. This +/// guarantees that no use of the returned reference ever produces invalid +/// values. pub const unsafe fn as_bytes_mut<'a, T>(v: &'a mut T) -> &'a mut [u8] { - // SAFETY: We retain the allocation size of `T` and its lifetime. Hence, - // the transmute is safe for as long as `'a`. Since `v` is - // borrowed, we claim mutable access for the entire lifetime of - // the returned value. + // SAFETY: Propagated to caller. unsafe { core::slice::from_raw_parts_mut::<'a, u8>( v as *mut _ as *mut _, @@ -206,9 +229,18 @@ pub const unsafe fn as_bytes_mut<'a, T>(v: &'a mut T) -> &'a mut [u8] { /// /// Compare the backing memory of two values for equality. Return `true` if the /// memory compares equal, false if not. Note that all memory must compare -/// equal, including padding bytes (which have no guaranteed nor stable value). -pub fn eq(a: &A, b: &B) -> bool { - *as_bytes(a) == *as_bytes(b) +/// equal. +/// +/// ## Safety +/// +/// The caller must guarantee that: +/// +/// - `A` and `B` have no padding bytes, and all bytes of `a` and `b` belong to +/// their respective type. This implies that all bytes in `a` and `b` are +/// initialized. +pub unsafe fn eq(a: &A, b: &B) -> bool { + // SAFETY: Propagated to caller. + unsafe { *as_bytes(a) == *as_bytes(b) } } #[cfg(test)] @@ -270,23 +302,39 @@ mod test { } } + // Verify uninit aliasing + #[test] + fn uninit_alias() { + let v: u16 = 0xf0f0; + + assert_eq!(v, 0xf0f0); + assert_eq!(unsafe { *as_uninit(&v).as_ptr() }, 0xf0f0); + + let v: [u16; 2] = [0xf0, 0xf1]; + + assert_eq!(v[0], 0xf0); + assert_eq!(v[1], 0xf1); + assert_eq!(unsafe { *slice_as_uninit(&v)[0].as_ptr() }, 0xf0); + assert_eq!(unsafe { *slice_as_uninit(&v)[1].as_ptr() }, 0xf1); + } + // Verify byte aliasing #[test] fn byte_alias() { let mut v: u16 = 0xf0f0; - assert_eq!(as_bytes(&v)[0], 0xf0); - assert_eq!(as_bytes(&v)[1], 0xf0); - unsafe { + assert_eq!(as_bytes(&v)[0], 0xf0); + assert_eq!(as_bytes(&v)[1], 0xf0); + as_bytes_mut(&mut v)[0] = 0x0f; as_bytes_mut(&mut v)[1] = 0x0f; - } - assert_eq!(as_bytes(&v)[0], 0x0f); - assert_eq!(as_bytes(&v)[1], 0x0f); + assert_eq!(as_bytes(&v)[0], 0x0f); + assert_eq!(as_bytes(&v)[1], 0x0f); - assert_eq!(v, 0x0f0f); + assert_eq!(v, 0x0f0f); + } } // Verify byte-wise equality @@ -294,11 +342,13 @@ mod test { fn byte_eq() { let v: u16 = 0xf0f0; - assert!(eq(&v, &0xf0f0u16)); - assert!(eq(&v, &[0xf0u8, 0xf0u8])); + unsafe { + assert!(eq(&v, &0xf0f0u16)); + assert!(eq(&v, &[0xf0u8, 0xf0u8])); - assert!(!eq(&v, &0x00f0u16)); - assert!(!eq(&v, &0xf0f0u32)); - assert!(!eq(&v, &[0xf0u16, 0xf0u16])); + assert!(!eq(&v, &0x00f0u16)); + assert!(!eq(&v, &0xf0f0u32)); + assert!(!eq(&v, &[0xf0u16, 0xf0u16])); + } } } diff --git a/lib/sys/src/ffi/linux/test.rs b/lib/sys/src/ffi/linux/test.rs index a768653..4772450 100644 --- a/lib/sys/src/ffi/linux/test.rs +++ b/lib/sys/src/ffi/linux/test.rs @@ -12,15 +12,19 @@ use native as libc; // Compare two `const` definitions for equality. This will compare their type // layout and memory content for equality. -fn eq_def_const(a: &A, b: &B) -> bool { - core::mem::size_of::() == core::mem::size_of::() - && core::mem::align_of::() == core::mem::align_of::() - && osi::mem::eq(a, b) +unsafe fn eq_def_const(a: &A, b: &B) -> bool { + // SAFETY: Propagated to caller. + unsafe { + core::mem::size_of::() == core::mem::size_of::() + && core::mem::align_of::() == core::mem::align_of::() + && osi::mem::eq(a, b) + } } // A 3-way variant of `eq_def_const()`. -fn eq3_def_const(a: &A, b: &B, c: &C) -> bool { - eq_def_const(a, b) && eq_def_const(a, c) +unsafe fn eq3_def_const(a: &A, b: &B, c: &C) -> bool { + // SAFETY: Propagated to caller. + unsafe { eq_def_const(a, b) && eq_def_const(a, c) } } // Verify that all supported platforms are available, by simply checking that @@ -37,5 +41,7 @@ fn platform_availability() { // Compare target APIs with native and libc APIs, and verify they match. #[test] fn comparison() { - assert!(eq3_def_const(&target::errno::EPERM, &native::errno::EPERM, &libc::errno::EPERM)); + unsafe { + assert!(eq3_def_const(&target::errno::EPERM, &native::errno::EPERM, &libc::errno::EPERM)); + } }