From c2f05807f802715cfe4039ae64814cad50488482 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Wed, 13 Sep 2023 00:19:36 +0000 Subject: [PATCH] Fix bug in validate_cast_and_convert_metadata Previously, `validate_cast_and_convert_metadata` incorrectly assumed that, for slice DSTs, the trailing slice element would always have an alignment at least as large as the outer type's alignment, and so there would never be any padding added after the trailing slice. In this commit, we rewrite `validate_cast_and_convert_metadata` to no longer assume this behavior. While we're here, we add the `test_validate_rust_layout` test, which validates many of our assumptions about Rust's type layout against the standard library to help catch mismatches like this in the future. --- .github/workflows/ci.yml | 23 +- cargo.sh | 8 +- src/lib.rs | 889 +++++++++++++++++++++------- src/third_party/rust/LICENSE-APACHE | 176 ++++++ src/third_party/rust/LICENSE-MIT | 23 + src/third_party/rust/layout.rs | 42 ++ src/util.rs | 3 + 7 files changed, 960 insertions(+), 204 deletions(-) create mode 100644 src/third_party/rust/LICENSE-APACHE create mode 100644 src/third_party/rust/LICENSE-MIT create mode 100644 src/third_party/rust/layout.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5190ff9a88..b90574d116 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,7 +150,12 @@ jobs: if: contains(matrix.target, 'i686') - name: Run tests - run: ./cargo.sh +${{ matrix.toolchain }} test --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose + run: | + ./cargo.sh +${{ matrix.toolchain }} test \ + --package ${{ matrix.crate }} \ + --target ${{ matrix.target }} \ + ${{ matrix.features }} \ + --verbose # Only run tests when targetting x86 (32- or 64-bit) - we're executing on # x86_64, so we can't run tests for any non-x86 target. # @@ -163,14 +168,24 @@ jobs: # Run under both the stacked borrows model (default) and under the tree # borrows model to ensure we're compliant with both. for EXTRA_FLAGS in "" "-Zmiri-tree-borrows"; do - # Skip the `ui` test since it invokes the compiler, which we can't do from - # Miri (and wouldn't want to do anyway). + # On `--skip ui`: Skip the `ui` test since it invokes the compiler, + # which we can't do from Miri (and wouldn't want to do anyway). + # + # On `matrix.features == '--all-features': We mark tests which are + # particularly expensive to run under Miri as + # `#[cfg_attr(miri, ignore)]`. We want to run them once per + # toolchain/target combination, but not once per feature set so we + # keep the expensive computation to a minimum. + # + # TODO(#391): Optimize these tests. MIRIFLAGS="$MIRIFLAGS $EXTRA_FLAGS" ./cargo.sh +${{ matrix.toolchain }} \ miri test \ --package ${{ matrix.crate }} \ --target ${{ matrix.target }} \ ${{ matrix.features }} \ - -- --skip ui + -- --skip ui \ + ${{ matrix.features == '--all-features' && '--ignored' || '' }} + done # Only nightly has a working Miri, so we skip installing on all other # toolchains. diff --git a/cargo.sh b/cargo.sh index 7dacf8449f..a214efca43 100755 --- a/cargo.sh +++ b/cargo.sh @@ -55,6 +55,10 @@ function lookup-version { esac } +function get-rustflags { + [ "$1" == nightly ] && echo "--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS" +} + case "$1" in # cargo.sh --version --version) @@ -67,14 +71,14 @@ case "$1" in for toolchain in msrv stable nightly; do echo "[cargo.sh] running with toolchain: $toolchain" >&2 TOOLCHAIN="$(lookup-version $toolchain)" - cargo "+$TOOLCHAIN" ${@:2} + RUSTFLAGS="$(get-rustflags $toolchain)" cargo "+$TOOLCHAIN" ${@:2} done exit 0 ;; # cargo.sh + [...] +*) TOOLCHAIN="$(lookup-version ${1:1})" - cargo "+$TOOLCHAIN" ${@:2} + RUSTFLAGS="$(get-rustflags ${1:1})" cargo "+$TOOLCHAIN" ${@:2} ;; *) print-usage-and-exit diff --git a/src/lib.rs b/src/lib.rs index 676220bf99..9ce59def56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -153,7 +153,10 @@ ))] #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "simd-nightly", feature(stdsimd))] - +#![cfg_attr( + __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, + feature(layout_for_ptr, pointer_byte_offsets, strict_provenance) +)] #[macro_use] mod macros; @@ -173,7 +176,6 @@ pub use crate::wrappers::*; pub use zerocopy_derive::*; use core::{ - alloc::Layout, cell::{self, RefMut}, cmp::Ordering, fmt::{self, Debug, Display, Formatter}, @@ -193,7 +195,7 @@ extern crate alloc; #[cfg(feature = "alloc")] use { alloc::{boxed::Box, vec::Vec}, - core::ptr::NonNull, + core::{alloc::Layout, ptr::NonNull}, }; // This is a hack to allow zerocopy-derive derives to work in this crate. They @@ -204,28 +206,90 @@ mod zerocopy { pub(crate) use crate::*; } +#[rustversion::nightly] +#[cfg(all(test, not(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)))] +const _: () = { + #[deprecated = "some tests may be skipped due to missing RUSTFLAGS=\"--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS\""] + const _WARNING: () = (); + #[warn(deprecated)] + _WARNING +}; + /// The layout of a type which might be dynamically-sized. /// -/// `DstLayout` describes the layout of sized types, slice types, and "custom +/// `DstLayout` describes the layout of sized types, slice types, and "slice /// DSTs" - ie, those that are known by the type system to have a trailing slice /// (as distinguished from `dyn Trait` types - such types *might* have a /// trailing slice type, but the type system isn't aware of it). +/// +/// # Safety +/// +/// Unlike [`core::alloc::Layout`], `DstLayout` is only used to describe full +/// Rust types - ie, those that satisfy the layout requirements outlined by +/// [the reference]. Callers may assume that an instance of `DstLayout` +/// satisfies any conditions imposed on Rust types by the reference. +/// +/// If `layout: DstLayout` describes a type, `T`, then it is guaranteed that: +/// - `layout.align` is equal to `T`'s alignment +/// - If `layout.size_info` is `SizeInfo::Sized { size }`, then `T: Sized` and +/// `size_of::() == size` +/// - If `layout.size_info` is `SizeInfo::SliceDst(slice_layout)`, then +/// - `T` is a slice DST +/// - The `size` of an instance of `T` with `elems` trailing slice elements is +/// equal to `slice_layout.offset + slice_layout.elem_size * elems` rounded up +/// to the nearest multiple of `layout.align`. Any bytes in the range +/// `[slice_layout.offset + slice_layout.elem_size * elems, size)` are padding +/// and must not be assumed to be initialized. +/// +/// [the reference]: https://doc.rust-lang.org/reference/type-layout.html #[doc(hidden)] #[allow(missing_debug_implementations, missing_copy_implementations)] #[cfg_attr(test, derive(Copy, Clone, Debug, PartialEq, Eq))] pub struct DstLayout { - /// The base size and the alignment of the type: - /// - For sized types, the size encoded by this `Layout` is - /// `size_of::()`. For DSTs, the size represents the size of the type - /// when the trailing slice field contains 0 elements. - /// - For all types, the alignment represents the alignment of the type. - // TODO: If we end up replacing this with separate size and alignment to - // make Kani happy, file an issue to eventually adopt the stdlib's - // `Alignment` type trick. - _base_layout: Layout, - /// For sized types, `None`. For DSTs, the size of the element type of the - /// trailing slice. - _trailing_slice_elem_size: Option, + _align: NonZeroUsize, + _size_info: SizeInfo, +} + +#[cfg_attr(test, derive(Copy, Clone, Debug, PartialEq, Eq))] +enum SizeInfo { + Sized { _size: usize }, + SliceDst(TrailingSliceLayout), +} + +#[cfg_attr(test, derive(Copy, Clone, Debug, PartialEq, Eq))] +struct TrailingSliceLayout { + // The offset of the first byte of the trailing slice field. Note that this + // is NOT the same as the minimum size of the type. For example, consider + // the following type: + // + // struct Foo { + // a: u16, + // b: u8, + // c: [u8], + // } + // + // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed + // by a padding byte. + _offset: usize, + // The size of the element type of the trailing slice field. + _elem_size: E, +} + +impl SizeInfo { + /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a + /// `NonZeroUsize`. If `elem_size` is 0, returns `None`. + const fn _try_to_nonzero_elem_size(&self) -> Option> { + Some(match *self { + SizeInfo::Sized { _size } => SizeInfo::Sized { _size }, + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { + if let Some(_elem_size) = NonZeroUsize::new(_elem_size) { + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) + } else { + return None; + } + } + }) + } } #[cfg_attr(test, derive(Copy, Clone, Debug))] @@ -241,7 +305,16 @@ impl DstLayout { /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. const fn for_type() -> DstLayout { - DstLayout { _base_layout: Layout::new::(), _trailing_slice_elem_size: None } + // SAFETY: `align` is correct by construction. `T: Sized`, and so it is + // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the + // `size` field is also correct by construction. + DstLayout { + _align: match NonZeroUsize::new(mem::align_of::()) { + Some(align) => align, + None => unreachable!(), + }, + _size_info: SizeInfo::Sized { _size: mem::size_of::() }, + } } /// Constructs a `DstLayout` which describes `[T]`. @@ -250,14 +323,23 @@ impl DstLayout { /// /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`. const fn for_slice() -> DstLayout { + // SAFETY: The alignment of a slice is equal to the alignment of its + // element type, and so `align` is initialized correctly. + // + // Since this is just a slice type, there is no offset between the + // beginning of the type and the beginning of the slice, so it is + // correct to set `offset: 0`. The `elem_size` is correct by + // construction. Since `[T]` is a (degenerate case of a) slice DST, it + // is correct to initialize `size_info` to `SizeInfo::SliceDst`. DstLayout { - // SAFETY: `[T; 0]` has the same alignment as `T`, but zero size. - // [1] A slice of length 0 has no size, so 0 is the correct size for - // the base of the type. - // - // [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout - _base_layout: Layout::new::<[T; 0]>(), - _trailing_slice_elem_size: Some(mem::size_of::()), + _align: match NonZeroUsize::new(mem::align_of::()) { + Some(align) => align, + None => unreachable!(), + }, + _size_info: SizeInfo::SliceDst(TrailingSliceLayout { + _offset: 0, + _elem_size: mem::size_of::(), + }), } } @@ -283,10 +365,9 @@ impl DstLayout { /// - A prefix cast is requested, and `addr` does not satisfy `self`'s /// alignment requirement /// - A suffix cast is requested, and `addr + bytes_len` does not satisfy - /// `self`'s alignment requirement (as a consequence, since the size of - /// the trailing slice element is a multiple of the alignment, no length - /// for the trailing slice will result in a starting address which is - /// properly aligned) + /// `self`'s alignment requirement (as a consequence, since all instances + /// of the type are a multiple of its alignment, no size for the type will + /// result in a starting address which is properly aligned) /// /// # Safety /// @@ -340,133 +421,135 @@ impl DstLayout { }; } - // Note that, in practice, `elem_size` is always a compile-time - // constant. We do this check earlier than needed to ensure that we - // always panic as a result of bugs in the program (such as calling this - // function on an invalid type) instead of allowing this panic to be - // hidden if the cast would have failed anyway for runtime reasons (such - // as a too-small memory region). + // Note that, in practice, `self` is always a compile-time constant. We + // do this check earlier than needed to ensure that we always panic as a + // result of bugs in the program (such as calling this function on an + // invalid type) instead of allowing this panic to be hidden if the cast + // would have failed anyway for runtime reasons (such as a too-small + // memory region). // // TODO(#67): Once our MSRV is 1.65, use let-else: // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements - let elem_size = match self._trailing_slice_elem_size { - Some(elem_size) => match NonZeroUsize::new(elem_size) { - Some(elem_size) => Some(elem_size), - None => panic!("attempted to cast to slice type with zero-sized element"), - }, - None => None, + let size_info = match self._size_info._try_to_nonzero_elem_size() { + Some(size_info) => size_info, + None => panic!("attempted to cast to slice type with zero-sized element"), }; // Precondition __debug_assert!(addr.checked_add(bytes_len).is_some(), "`addr` + `bytes_len` > usize::MAX"); - // We check alignment for `addr` (for prefix casts) or `addr + - // bytes_len` (for suffix casts). For a prefix cast, the correctness of - // this check is trivial - `addr` is the address the object will live - // at. - // - // For a suffix cast, we know that all valid sizes for the type are a - // multiple of the alignment. Thus, a validly-sized instance which lives - // at a validly-aligned address must also end at a validly-aligned - // address. Thus, if the end address for a suffix cast (`addr + - // bytes_len`) is not aligned, then no valid start address will be - // aligned either. - let offset = match cast_type { - _CastType::_Prefix => 0, - _CastType::_Suffix => bytes_len, - }; - - // Addition is guaranteed not to overflow because `offset <= bytes_len`, - // and `addr + bytes_len <= usize::MAX` is a precondition of this - // method. Modulus is guaranteed not to divide by 0 because `.align()` - // guarantees that its return value is non-zero. - #[allow(clippy::arithmetic_side_effects)] - if (addr + offset) % self._base_layout.align() != 0 { - return None; - } - - let base_size = self._base_layout.size(); - - // LEMMA 0: max_slice_bytes + base_size == bytes_len - // - // LEMMA 1: base_size <= bytes_len: - // - If `base_size > bytes_len`, `bytes_len.checked_sub(base_size)` - // returns `None`, and we return. - // - // TODO(#67): Once our MSRV is 1.65, use let-else: - // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements - let max_slice_bytes = if let Some(max_byte_slice) = bytes_len.checked_sub(base_size) { - max_byte_slice - } else { - return None; - }; - - // Lemma 0 - __debug_assert!(max_slice_bytes + base_size == bytes_len); - - // Lemma 1 - __debug_assert!(base_size <= bytes_len); - - let (elems, self_bytes) = if let Some(elem_size) = elem_size { - // Guaranteed not to divide by 0 because `elem_size` is a - // `NonZeroUsize`. - #[allow(clippy::arithmetic_side_effects)] - let elems = max_slice_bytes / elem_size.get(); - - // NOTE: Another option for this step in the algorithm is to set - // `slice_bytes = elems * elem_size`. However, using multiplication - // causes Kani to choke. In practice, the compiler is likely to - // generate identical machine code in both cases. Note that this - // divide-then-mod approach is trivially optimizable into a single - // operation that computes both the quotient and the remainder. - - // First line is guaranteed not to mod by 0 because `elem_size` is a - // `NonZeroUsize`. Second line is guaranteed not to underflow - // because `rem <= max_slice_bytes` thanks to the mod operation. + // Alignment checks go in their own block to avoid introducing variables + // into the top-level scope. + { + // We check alignment for `addr` (for prefix casts) or `addr + + // bytes_len` (for suffix casts). For a prefix cast, the correctness + // of this check is trivial - `addr` is the address the object will + // live at. // - // LEMMA 2: slice_bytes <= max_slice_bytes - #[allow(clippy::arithmetic_side_effects)] - let rem = max_slice_bytes % elem_size.get(); - #[allow(clippy::arithmetic_side_effects)] - let slice_bytes = max_slice_bytes - rem; - - // Lemma 2 - __debug_assert!(slice_bytes <= max_slice_bytes); - - // Guaranteed not to overflow: - // - max_slice_bytes + base_size == bytes_len (lemma 0) - // - slice_bytes <= max_slice_bytes (lemma 2) - // - slice_bytes + base_size <= bytes_len (substitution) ------+ - // - bytes_len <= usize::MAX (bytes_len: usize) | - // - slice_bytes + base_size <= usize::MAX (substitution) | - // | - // LEMMA 3: self_bytes <= bytes_len: | - // - slice_bytes + base_size <= bytes_len <--------------------------+ (reused for lemma) - // - slice_bytes <= bytes_len - #[allow(clippy::arithmetic_side_effects)] - let self_bytes = base_size + slice_bytes; + // For a suffix cast, we know that all valid sizes for the type are + // a multiple of the alignment (and by safety precondition, we know + // `DstLayout` may only describe valid Rust types). Thus, a + // validly-sized instance which lives at a validly-aligned address + // must also end at a validly-aligned address. Thus, if the end + // address for a suffix cast (`addr + bytes_len`) is not aligned, + // then no valid start address will be aligned either. + let offset = match cast_type { + _CastType::_Prefix => 0, + _CastType::_Suffix => bytes_len, + }; - // Lemma 3 - __debug_assert!(self_bytes <= bytes_len); + // Addition is guaranteed not to overflow because `offset <= + // bytes_len`, and `addr + bytes_len <= usize::MAX` is a + // precondition of this method. Modulus is guaranteed not to divide + // by 0 because `align` is non-zero. + #[allow(clippy::arithmetic_side_effects)] + if (addr + offset) % self._align.get() != 0 { + return None; + } + } - (elems, self_bytes) - } else { - (0, base_size) + let (elems, self_bytes) = match size_info { + SizeInfo::Sized { _size: size } => { + if size > bytes_len { + return None; + } + (0, size) + } + SizeInfo::SliceDst(TrailingSliceLayout { _offset: offset, _elem_size: elem_size }) => { + let align = self._align.get(); + + // Calculate the maximum number of bytes that could be consumed + // - any number of bytes larger than this will either not be a + // multiple of the alignment, or will be larger than + // `bytes_len`. + // + // Guaranteed not to: + // - divide by zero: `align` is non-zero. + // - overflow on multiplication: `usize::MAX >= bytes_len >= + // (bytes_len / align) * align` + // + // TODO(#390): Compute this more efficiently. + #[allow(clippy::arithmetic_side_effects)] + let max_total_bytes = (bytes_len / align) * align; + // Calculate the maximum number of bytes that could be consumed + // by the trailing slice. + // + // TODO(#67): Once our MSRV is 1.65, use let-else: + // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements + let max_slice_and_padding_bytes = match max_total_bytes.checked_sub(offset) { + Some(max) => max, + // `bytes_len` too small even for 0 trailing slice elements. + None => return None, + }; + + // Calculate the number of elements that fit in + // `max_slice_and_padding_bytes`; any remaining bytes will be + // considered padding. + // + // Guaranteed not to divide by zero: `elem_size` is non-zero. + #[allow(clippy::arithmetic_side_effects)] + let elems = max_slice_and_padding_bytes / elem_size.get(); + // Guaranteed not to overflow on multiplication: `usize::MAX >= + // max_slice_and_padding_bytes >= (max_slice_and_padding_bytes / + // elem_size) * elem_size`. + // + // Guaranteed not to overflow on addition: + // - max_slice_and_padding_bytes == max_total_bytes - offset + // - elems * elem_size <= max_slice_and_padding_bytes == max_total_bytes - offset + // - elems * elem_size + offset <= max_total_bytes <= usize::MAX + #[allow(clippy::arithmetic_side_effects)] + let without_padding = offset + elems * elem_size.get(); + // `self_bytes` is equal to the offset bytes plus the bytes + // consumed by the trailing slice plus any padding bytes + // required to satisfy the alignment. Note that we have computed + // the maximum number of trailing slice elements that could fit + // in `self_bytes`, so any padding is guaranteed to be less than + // the size of an extra element. + // + // Guaranteed not to overflow: + // - By previous comment: without_padding == elems * elem_size + + // offset <= max_total_bytes + // - By construction, `max_total_bytes` is a multiple of + // `self._align`. + // - At most, adding padding needed to round `without_padding` + // up to the next multiple of the alignment will bring + // `self_bytes` up to `max_total_bytes`. + #[allow(clippy::arithmetic_side_effects)] + let self_bytes = without_padding + + util::core_layout::_padding_needed_for(without_padding, self._align); + (elems, self_bytes) + } }; - // LEMMA 4: self_bytes <= bytes_len: - // - `if` branch returns `self_bytes`; lemma 3 guarantees `self_bytes <= - // bytes_len` - // - `else` branch returns `base_size`; lemma 1 guarantees `base_size <= - // bytes_len` - - // Lemma 4 __debug_assert!(self_bytes <= bytes_len); let split_at = match cast_type { _CastType::_Prefix => self_bytes, - // Guaranteed not to underflow because `self_bytes <= bytes_len` - // (lemma 4). + // Guaranteed not to underflow: + // - In the `Sized` branch, only returns `size` if `size <= + // bytes_len`. + // - In the `SliceDst` branch, calculates `self_bytes <= + // max_toatl_bytes`, which is upper-bounded by `bytes_len`. #[allow(clippy::arithmetic_side_effects)] _CastType::_Suffix => bytes_len - self_bytes, }; @@ -2976,17 +3059,22 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn test_validate_cast_and_convert_metadata() { - fn layout( - base_size: usize, - align: usize, - _trailing_slice_elem_size: Option, - ) -> DstLayout { - DstLayout { - _base_layout: Layout::from_size_align(base_size, align).unwrap(), - _trailing_slice_elem_size, + impl From for SizeInfo { + fn from(_size: usize) -> SizeInfo { + SizeInfo::Sized { _size } } } + impl From<(usize, usize)> for SizeInfo { + fn from((_offset, _elem_size): (usize, usize)) -> SizeInfo { + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) + } + } + + fn layout>(s: S, align: usize) -> DstLayout { + DstLayout { _size_info: s.into(), _align: NonZeroUsize::new(align).unwrap() } + } + /// This macro accepts arguments in the form of: /// /// layout(_, _, _).validate(_, _, _), Ok(Some((_, _))) @@ -3024,30 +3112,64 @@ mod tests { /// `a..b`). In this case, wrap the expression in parentheses, and it /// will become valid `tt`. macro_rules! test { - ( - layout($base_size:tt, $align:tt, $trailing_size:tt) + ($(:$sizes:expr =>)? + layout($size:tt, $align:tt) .validate($addr:tt, $bytes_len:tt, $cast_type:tt), $expect:pat $(,)? ) => { itertools::iproduct!( - test!(@generate_usize $base_size), + test!(@generate_size $size), test!(@generate_align $align), - test!(@generate_opt_usize $trailing_size), test!(@generate_usize $addr), test!(@generate_usize $bytes_len), test!(@generate_cast_type $cast_type) - ).for_each(|(base_size, align, trailing_size, addr, bytes_len, cast_type)| { + ).for_each(|(size_info, align, addr, bytes_len, cast_type)| { + // Temporarily disable the panic hook installed by the test + // harness. If we don't do this, all panic messages will be + // kept in an internal log. On its own, this isn't a + // problem, but if a non-caught panic ever happens (ie, in + // code later in this test not in this macro), all of the + // previously-buffered messages will be dumped, hiding the + // real culprit. + let previous_hook = std::panic::take_hook(); + // I don't understand why, but this seems to be required in + // addition to the previous line. + std::panic::set_hook(Box::new(|_| {})); let actual = std::panic::catch_unwind(|| { - layout(base_size, align, trailing_size)._validate_cast_and_convert_metadata(addr, bytes_len, cast_type) + layout(size_info, align)._validate_cast_and_convert_metadata(addr, bytes_len, cast_type) }).map_err(|d| { *d.downcast::<&'static str>().expect("expected string panic message").as_ref() }); + std::panic::set_hook(previous_hook); + assert_matches::assert_matches!( actual, $expect, - "layout({base_size}, {align}, {trailing_size:?}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?})", + "layout({size_info:?}, {align}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?})", ); }); }; (@generate_usize _) => { 0..8 }; + // Generate sizes for both Sized and !Sized types. + (@generate_size _) => { + test!(@generate_size (_)).chain(test!(@generate_size (_, _))) + }; + // Generate sizes for both Sized and !Sized types by chaining + // specified iterators for each. + (@generate_size ($sized_sizes:tt | $unsized_sizes:tt)) => { + test!(@generate_size ($sized_sizes)).chain(test!(@generate_size $unsized_sizes)) + }; + // Generate sizes for Sized types. + (@generate_size (_)) => { test!(@generate_size (0..8)) }; + (@generate_size ($sizes:expr)) => { $sizes.into_iter().map(Into::::into) }; + // Generate sizes for !Sized types. + (@generate_size ($min_sizes:tt, $elem_sizes:tt)) => { + itertools::iproduct!( + test!(@generate_min_size $min_sizes), + test!(@generate_elem_size $elem_sizes) + ).map(Into::::into) + }; + (@generate_fixed_size _) => { (0..8).into_iter().map(Into::::into) }; + (@generate_min_size _) => { 0..8 }; + (@generate_elem_size _) => { 1..8 }; (@generate_align _) => { [1, 2, 4, 8, 16] }; (@generate_opt_usize _) => { [None].into_iter().chain((0..8).map(Some).into_iter()) }; (@generate_cast_type _) => { [_CastType::_Prefix, _CastType::_Suffix] }; @@ -3060,21 +3182,20 @@ mod tests { (@$_:ident $vals:expr) => { $vals }; } - const EVENS: [usize; 5] = [0, 2, 4, 6, 8]; - const NZ_EVENS: [usize; 5] = [2, 4, 6, 8, 10]; - const ODDS: [usize; 5] = [1, 3, 5, 7, 9]; + const EVENS: [usize; 8] = [0, 2, 4, 6, 8, 10, 12, 14]; + const ODDS: [usize; 8] = [1, 3, 5, 7, 9, 11, 13, 15]; // base_size is too big for the memory region. - test!(layout((1..8), _, ((1..8).map(Some))).validate(_, [0], _), Ok(None)); - test!(layout((2..8), _, ((1..8).map(Some))).validate(_, [1], _), Ok(None)); + test!(layout(((1..8) | ((1..8), (1..8))), _).validate(_, [0], _), Ok(None)); + test!(layout(((2..8) | ((2..8), (2..8))), _).validate(_, [1], _), Ok(None)); // addr is unaligned for prefix cast - test!(layout(_, [2], [None]).validate(ODDS, _, _Prefix), Ok(None)); - test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(ODDS, _, _Prefix), Ok(None)); + test!(layout(_, [2]).validate(ODDS, _, _Prefix), Ok(None)); + test!(layout(_, [2]).validate(ODDS, _, _Prefix), Ok(None)); // addr is aligned, but end of buffer is unaligned for suffix cast - test!(layout(_, [2], [None]).validate(EVENS, ODDS, _Suffix), Ok(None)); - test!(layout(_, [2], (NZ_EVENS.map(Some))).validate(EVENS, ODDS, _Suffix), Ok(None)); + test!(layout(_, [2]).validate(EVENS, ODDS, _Suffix), Ok(None)); + test!(layout(_, [2]).validate(EVENS, ODDS, _Suffix), Ok(None)); // Unfortunately, these constants cannot easily be used in the // implementation of `validate_cast_and_convert_metadata`, since @@ -3092,16 +3213,13 @@ mod tests { } // casts with ZST trailing element types are unsupported - test!(layout(_, _, [Some(0)]).validate(_, _, _), Err(msgs::TRAILING),); + test!(layout((_, [0]), _).validate(_, _, _), Err(msgs::TRAILING),); // addr + bytes_len must not overflow usize + test!(layout(_, _).validate([usize::MAX], (1..100), _), Err(msgs::OVERFLOW)); + test!(layout(_, _).validate((1..100), [usize::MAX], _), Err(msgs::OVERFLOW)); test!( - layout(_, [1], (NZ_EVENS.map(Some))).validate([usize::MAX], (1..100), _), - Err(msgs::OVERFLOW) - ); - test!(layout(_, [1], [None]).validate((1..100), [usize::MAX], _), Err(msgs::OVERFLOW)); - test!( - layout([1], [1], [None]).validate( + layout(_, _).validate( [usize::MAX / 2 + 1, usize::MAX], [usize::MAX / 2 + 1, usize::MAX], _ @@ -3118,35 +3236,42 @@ mod tests { if let Some((elems, split_at)) = layout._validate_cast_and_convert_metadata(addr, bytes_len, cast_type) { - let (base_size, align, trailing_elem_size) = ( - layout._base_layout.size(), - layout._base_layout.align(), - layout._trailing_slice_elem_size, - ); - + let (size_info, align) = (layout._size_info, layout._align); let debug_str = format!( - "layout({base_size}, {align}, {trailing_elem_size:?}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?}) => ({elems}, {split_at})", + "layout({size_info:?}, {align}).validate_cast_and_convert_metadata({addr}, {bytes_len}, {cast_type:?}) => ({elems}, {split_at})", ); // If this is a sized type (no trailing slice), then `elems` is // meaningless, but in practice we set it to 0. Callers are not // allowed to rely on this, but a lot of math is nicer if // they're able to, and some callers might accidentally do that. - assert!(!(trailing_elem_size.is_none() && elems != 0), "{}", debug_str); - - let resulting_size = base_size + (elems * trailing_elem_size.unwrap_or(0)); - // Test that, for unsized types, `validate_cast_and_convert_metadata` computed the - // largest possible value that fits in the given byte range. - assert!( - trailing_elem_size - .map(|elem_size| resulting_size + elem_size > bytes_len) - .unwrap_or(true), - "{}", - debug_str - ); + let sized = matches!(layout._size_info, SizeInfo::Sized { .. }); + assert!(!(sized && elems != 0), "{}", debug_str); + + let resulting_size = match layout._size_info { + SizeInfo::Sized { _size } => _size, + SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }) => { + let padded_size = |elems| { + let without_padding = offset + elems * elem_size; + without_padding + + util::core_layout::_padding_needed_for(without_padding, align) + }; + + let resulting_size = padded_size(elems); + // Test that `validate_cast_and_convert_metadata` + // computed the largest possible value that fits in the + // given range. + assert!(padded_size(elems + 1) > bytes_len, "{}", debug_str); + resulting_size + } + }; - // Test safety postconditions guaranteed by `validate_cast_and_convert_metadata`. - assert!(resulting_size <= bytes_len); + // Test safety postconditions guaranteed by + // `validate_cast_and_convert_metadata`. + assert!(resulting_size <= bytes_len, "{}", debug_str); match cast_type { _CastType::_Prefix => { assert_eq!(addr % align, 0, "{}", debug_str); @@ -3160,15 +3285,377 @@ mod tests { } } - let layouts = itertools::iproduct!(0..8, [1, 2, 4, 8], (1..8).map(Some).chain([None])) - .filter(|(size, align, trailing_elem_size)| { - size % align == 0 && trailing_elem_size.unwrap_or(*align) % align == 0 - }) - .map(|(s, a, t)| layout(s, a, t)); + let sizes = 0..8; + let elem_sizes = 1..8; + let size_infos = sizes + .clone() + .map(Into::::into) + .chain(itertools::iproduct!(sizes, elem_sizes).map(Into::::into)); + let layouts = itertools::iproduct!(size_infos, [1, 2, 4, 8, 16, 32]) + .filter(|(size_info, align)| !matches!(size_info, SizeInfo::Sized { _size } if _size % align != 0)) + .map(|(size_info, align)| layout(size_info, align)); itertools::iproduct!(layouts, 0..8, 0..8, [_CastType::_Prefix, _CastType::_Suffix]) .for_each(validate_behavior); } + // TODO(#391): Optimize this test so we it consumes less time under Miri in + // CI. + #[test] + #[cfg_attr(miri, ignore)] + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + fn test_validate_rust_layout() { + use core::ptr::NonNull; + + // This test synthesizes pointers with various metadata and uses Rust's + // built-in APIs to confirm that Rust makes decisions about type layout + // which are consistent with what we believe is guaranteed by the + // language. If this test fails, it doesn't just mean our code is wrong + // - it means we're misunderstanding the language's guarantees. + + #[derive(Debug)] + struct MacroArgs { + offset: usize, + align: NonZeroUsize, + elem_size: Option, + } + + /// # Safety + /// + /// `test` promises to only call `addr_of_slice_field` on a `NonNull` + /// which points to a valid `T`. + /// + /// `with_elems` must produce a pointer which points to a valid `T`. + fn test NonNull>( + args: MacroArgs, + with_elems: W, + addr_of_slice_field: Option) -> NonNull>, + ) { + let dst = args.elem_size.is_some(); + let layout = { + let size_info = match args.elem_size { + Some(elem_size) => SizeInfo::SliceDst(TrailingSliceLayout { + _offset: args.offset, + _elem_size: elem_size, + }), + None => SizeInfo::Sized { + // Rust only supports types whose sizes are a multiple + // of their alignment. If the macro created a type like + // this: + // + // #[repr(C, align(2))] + // struct Foo([u8; 1]); + // + // ...then Rust will automatically round the type's size + // up to 2. + _size: args.offset + + util::core_layout::_padding_needed_for(args.offset, args.align), + }, + }; + DstLayout { _size_info: size_info, _align: args.align } + }; + + const MAX_ELEMS: usize = if cfg!(miri) { 16 } else { 128 }; + for elems in 0..MAX_ELEMS { + let ptr = with_elems(elems); + + if let Some(addr_of_slice_field) = addr_of_slice_field { + let slc_field_ptr = addr_of_slice_field(ptr).as_ptr(); + // SAFETY: Both `slc_field_ptr` and `ptr` are pointers to + // the same valid Rust object. + // + // TODO(https://github.com/rust-lang/rust-clippy/issues/11534): + // Remove this `allow` once Clippy recognizes this block as + // having a safety comment. + #[allow(clippy::undocumented_unsafe_blocks)] + let offset: usize = + unsafe { slc_field_ptr.byte_offset_from(ptr.as_ptr()).try_into().unwrap() }; + assert_eq!(offset, args.offset); + } + + // SAFETY: `ptr` points to a valid `T`. + let (size, align) = unsafe { + (mem::size_of_val_raw(ptr.as_ptr()), mem::align_of_val_raw(ptr.as_ptr())) + }; + + // Avoid expensive allocation when running under Miri. + let assert_msg = if cfg!(miri) { + format!("\n{args:?}\nsize:{size}, align:{align}") + } else { + String::new() + }; + + let without_padding = + args.offset + args.elem_size.map(|elem_size| elems * elem_size).unwrap_or(0); + assert!(size >= without_padding, "{}", assert_msg); + assert_eq!(align, args.align.get(), "{}", assert_msg); + + // This encodes the most important part of the test: our + // understanding of how Rust determines the layout of repr(C) + // types. Sized repr(C) types are trivial, but DST types have + // some subtlety. Note that: + // - For sized types, `without_padding` is just the size of the + // type that we constructed for `Foo`. Since we may have + // requested a larger alignment, `Foo` may actually be larger + // than this, hence `padding_needed_for`. + // - For unsized types, `without_padding` is dynamically + // computed from the offset, the element size, and element + // count. We expect that the size of the object should be + // `offset + elem_size * elems` rounded up to the next + // alignment. + let expected_size = without_padding + + util::core_layout::_padding_needed_for(without_padding, args.align); + assert_eq!(expected_size, size, "{}", assert_msg); + + // For zero-sized element types, + // `validate_cast_and_convert_metadata` just panics, so we skip + // testing those types. + if args.elem_size.map(|elem_size| elem_size > 0).unwrap_or(true) { + let addr = ptr.addr().get(); + let (got_elems, got_split_at) = layout + ._validate_cast_and_convert_metadata(addr, size, _CastType::_Prefix) + .unwrap(); + // Avoid expensive allocation when running under Miri. + let assert_msg = if cfg!(miri) { + format!( + "{}\nvalidate_cast_and_convert_metadata({addr}, {size})", + assert_msg + ) + } else { + String::new() + }; + assert_eq!(got_split_at, size, "{}", assert_msg); + if dst { + assert!(got_elems >= elems, "{}", assert_msg); + if got_elems != elems { + // If `validate_cast_and_convert_metadata` + // returned more elements than `elems`, that + // means that `elems` is not the maximum number + // of elements that can fit in `size` - in other + // words, there is enough padding at the end of + // the value to fit at least one more element. + // If we use this metadata to synthesize a + // pointer, despite having a different element + // count, we still expect it to have the same + // size. + let got_ptr = with_elems(got_elems); + // SAFETY: `got_ptr` is a pointer to a valid `T`. + let size_of_got_ptr = unsafe { mem::size_of_val_raw(got_ptr.as_ptr()) }; + assert_eq!(size_of_got_ptr, size, "{}", assert_msg); + } + } else { + // For sized casts, the returned element value is + // technically meaningless, and we don't guarantee any + // particular value. In practice, it's always zero. + assert_eq!(got_elems, 0, "{}", assert_msg) + } + } + } + } + + macro_rules! validate_against_rust { + ($offset:literal, $align:literal $(, $elem_size:literal)?) => {{ + #[repr(C, align($align))] + struct Foo([u8; $offset]$(, [[u8; $elem_size]])?); + + let args = MacroArgs { + offset: $offset, + align: $align.try_into().unwrap(), + elem_size: { + #[allow(unused)] + let ret = None::; + $(let ret = Some($elem_size);)? + ret + } + }; + + #[repr(C, align($align))] + struct FooAlign; + // Create an aligned buffer to use in order to synthesize + // pointers to `Foo`. We don't ever load values from these + // pointers - we just do arithmetic on them - so having a "real" + // block of memory as opposed to a validly-aligned-but-dangling + // pointer is only necessary to make Miri happy since we run it + // with "strict provenance" checking enabled. + let aligned_buf = Align::<_, FooAlign>::new([0u8; 1024]); + let with_elems = |elems| { + let slc = NonNull::slice_from_raw_parts(NonNull::from(&aligned_buf.t), elems); + #[allow(clippy::as_conversions)] + NonNull::new(slc.as_ptr() as *mut Foo).unwrap() + }; + let addr_of_slice_field = { + #[allow(unused)] + let f = None::) -> NonNull>; + $( + // SAFETY: `test` promises to only call `f` with a `ptr` + // to a valid `Foo`. + let f: Option) -> NonNull> = Some(|ptr: NonNull| unsafe { + NonNull::new(ptr::addr_of_mut!((*ptr.as_ptr()).1)).unwrap().cast::() + }); + let _ = $elem_size; + )? + f + }; + + test::(args, with_elems, addr_of_slice_field); + }}; + } + + // Every permutation of: + // - offset in [0, 4] + // - align in [1, 16] + // - elem_size in [0, 4] (plus no elem_size) + validate_against_rust!(0, 1); + validate_against_rust!(0, 1, 0); + validate_against_rust!(0, 1, 1); + validate_against_rust!(0, 1, 2); + validate_against_rust!(0, 1, 3); + validate_against_rust!(0, 1, 4); + validate_against_rust!(0, 2); + validate_against_rust!(0, 2, 0); + validate_against_rust!(0, 2, 1); + validate_against_rust!(0, 2, 2); + validate_against_rust!(0, 2, 3); + validate_against_rust!(0, 2, 4); + validate_against_rust!(0, 4); + validate_against_rust!(0, 4, 0); + validate_against_rust!(0, 4, 1); + validate_against_rust!(0, 4, 2); + validate_against_rust!(0, 4, 3); + validate_against_rust!(0, 4, 4); + validate_against_rust!(0, 8); + validate_against_rust!(0, 8, 0); + validate_against_rust!(0, 8, 1); + validate_against_rust!(0, 8, 2); + validate_against_rust!(0, 8, 3); + validate_against_rust!(0, 8, 4); + validate_against_rust!(0, 16); + validate_against_rust!(0, 16, 0); + validate_against_rust!(0, 16, 1); + validate_against_rust!(0, 16, 2); + validate_against_rust!(0, 16, 3); + validate_against_rust!(0, 16, 4); + validate_against_rust!(1, 1); + validate_against_rust!(1, 1, 0); + validate_against_rust!(1, 1, 1); + validate_against_rust!(1, 1, 2); + validate_against_rust!(1, 1, 3); + validate_against_rust!(1, 1, 4); + validate_against_rust!(1, 2); + validate_against_rust!(1, 2, 0); + validate_against_rust!(1, 2, 1); + validate_against_rust!(1, 2, 2); + validate_against_rust!(1, 2, 3); + validate_against_rust!(1, 2, 4); + validate_against_rust!(1, 4); + validate_against_rust!(1, 4, 0); + validate_against_rust!(1, 4, 1); + validate_against_rust!(1, 4, 2); + validate_against_rust!(1, 4, 3); + validate_against_rust!(1, 4, 4); + validate_against_rust!(1, 8); + validate_against_rust!(1, 8, 0); + validate_against_rust!(1, 8, 1); + validate_against_rust!(1, 8, 2); + validate_against_rust!(1, 8, 3); + validate_against_rust!(1, 8, 4); + validate_against_rust!(1, 16); + validate_against_rust!(1, 16, 0); + validate_against_rust!(1, 16, 1); + validate_against_rust!(1, 16, 2); + validate_against_rust!(1, 16, 3); + validate_against_rust!(1, 16, 4); + validate_against_rust!(2, 1); + validate_against_rust!(2, 1, 0); + validate_against_rust!(2, 1, 1); + validate_against_rust!(2, 1, 2); + validate_against_rust!(2, 1, 3); + validate_against_rust!(2, 1, 4); + validate_against_rust!(2, 2); + validate_against_rust!(2, 2, 0); + validate_against_rust!(2, 2, 1); + validate_against_rust!(2, 2, 2); + validate_against_rust!(2, 2, 3); + validate_against_rust!(2, 2, 4); + validate_against_rust!(2, 4); + validate_against_rust!(2, 4, 0); + validate_against_rust!(2, 4, 1); + validate_against_rust!(2, 4, 2); + validate_against_rust!(2, 4, 3); + validate_against_rust!(2, 4, 4); + validate_against_rust!(2, 8); + validate_against_rust!(2, 8, 0); + validate_against_rust!(2, 8, 1); + validate_against_rust!(2, 8, 2); + validate_against_rust!(2, 8, 3); + validate_against_rust!(2, 8, 4); + validate_against_rust!(2, 16); + validate_against_rust!(2, 16, 0); + validate_against_rust!(2, 16, 1); + validate_against_rust!(2, 16, 2); + validate_against_rust!(2, 16, 3); + validate_against_rust!(2, 16, 4); + validate_against_rust!(3, 1); + validate_against_rust!(3, 1, 0); + validate_against_rust!(3, 1, 1); + validate_against_rust!(3, 1, 2); + validate_against_rust!(3, 1, 3); + validate_against_rust!(3, 1, 4); + validate_against_rust!(3, 2); + validate_against_rust!(3, 2, 0); + validate_against_rust!(3, 2, 1); + validate_against_rust!(3, 2, 2); + validate_against_rust!(3, 2, 3); + validate_against_rust!(3, 2, 4); + validate_against_rust!(3, 4); + validate_against_rust!(3, 4, 0); + validate_against_rust!(3, 4, 1); + validate_against_rust!(3, 4, 2); + validate_against_rust!(3, 4, 3); + validate_against_rust!(3, 4, 4); + validate_against_rust!(3, 8); + validate_against_rust!(3, 8, 0); + validate_against_rust!(3, 8, 1); + validate_against_rust!(3, 8, 2); + validate_against_rust!(3, 8, 3); + validate_against_rust!(3, 8, 4); + validate_against_rust!(3, 16); + validate_against_rust!(3, 16, 0); + validate_against_rust!(3, 16, 1); + validate_against_rust!(3, 16, 2); + validate_against_rust!(3, 16, 3); + validate_against_rust!(3, 16, 4); + validate_against_rust!(4, 1); + validate_against_rust!(4, 1, 0); + validate_against_rust!(4, 1, 1); + validate_against_rust!(4, 1, 2); + validate_against_rust!(4, 1, 3); + validate_against_rust!(4, 1, 4); + validate_against_rust!(4, 2); + validate_against_rust!(4, 2, 0); + validate_against_rust!(4, 2, 1); + validate_against_rust!(4, 2, 2); + validate_against_rust!(4, 2, 3); + validate_against_rust!(4, 2, 4); + validate_against_rust!(4, 4); + validate_against_rust!(4, 4, 0); + validate_against_rust!(4, 4, 1); + validate_against_rust!(4, 4, 2); + validate_against_rust!(4, 4, 3); + validate_against_rust!(4, 4, 4); + validate_against_rust!(4, 8); + validate_against_rust!(4, 8, 0); + validate_against_rust!(4, 8, 1); + validate_against_rust!(4, 8, 2); + validate_against_rust!(4, 8, 3); + validate_against_rust!(4, 8, 4); + validate_against_rust!(4, 16); + validate_against_rust!(4, 16, 0); + validate_against_rust!(4, 16, 1); + validate_against_rust!(4, 16, 2); + validate_against_rust!(4, 16, 3); + validate_against_rust!(4, 16, 4); + } + #[test] fn test_known_layout() { // Test that `$ty` and `ManuallyDrop<$ty>` have the expected layout. @@ -3183,9 +3670,15 @@ mod tests { }; } - let layout = |base_size, align, _trailing_slice_elem_size| DstLayout { - _base_layout: Layout::from_size_align(base_size, align).unwrap(), - _trailing_slice_elem_size, + let layout = |offset, align, _trailing_slice_elem_size| DstLayout { + _align: NonZeroUsize::new(align).unwrap(), + _size_info: match _trailing_slice_elem_size { + None => SizeInfo::Sized { _size: offset }, + Some(elem_size) => SizeInfo::SliceDst(TrailingSliceLayout { + _offset: offset, + _elem_size: elem_size, + }), + }, }; test!((), layout(0, 1, None)); diff --git a/src/third_party/rust/LICENSE-APACHE b/src/third_party/rust/LICENSE-APACHE new file mode 100644 index 0000000000..1b5ec8b78e --- /dev/null +++ b/src/third_party/rust/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/src/third_party/rust/LICENSE-MIT b/src/third_party/rust/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/src/third_party/rust/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/third_party/rust/layout.rs b/src/third_party/rust/layout.rs new file mode 100644 index 0000000000..dae9a860ee --- /dev/null +++ b/src/third_party/rust/layout.rs @@ -0,0 +1,42 @@ +use core::num::NonZeroUsize; + +/// Returns the amount of padding we must insert after `len` bytes to ensure +/// that the following address will satisfy `align` (measured in bytes). +/// +/// e.g., if `len` is 9, then `padding_needed_for(len, 4)` returns 3, because +/// that is the minimum number of bytes of padding required to get a 4-aligned +/// address (assuming that the corresponding memory block starts at a 4-aligned +/// address). +/// +/// The return value of this function has no meaning if `align` is not a +/// power-of-two. +/// +/// # Panics +/// +/// May panic if `align` is not a power of two. +#[inline(always)] +pub(crate) const fn _padding_needed_for(len: usize, align: NonZeroUsize) -> usize { + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask with `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let align = align.get(); + debug_assert!(align.is_power_of_two()); + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + len_rounded_up.wrapping_sub(len) +} diff --git a/src/util.rs b/src/util.rs index ed810dcb1c..06b5ef9a48 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#[path = "third_party/rust/layout.rs"] +pub(crate) mod core_layout; + use core::mem; pub(crate) trait AsAddress {