zerocopy/
layout.rs

1// Copyright 2024 The Fuchsia Authors
2//
3// Licensed under the 2-Clause BSD License <LICENSE-BSD or
4// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
5// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
6// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
7// This file may not be copied, modified, or distributed except according to
8// those terms.
9
10use core::{mem, num::NonZeroUsize};
11
12use crate::util;
13
14/// The target pointer width, counted in bits.
15const POINTER_WIDTH_BITS: usize = mem::size_of::<usize>() * 8;
16
17/// The layout of a type which might be dynamically-sized.
18///
19/// `DstLayout` describes the layout of sized types, slice types, and "slice
20/// DSTs" - ie, those that are known by the type system to have a trailing slice
21/// (as distinguished from `dyn Trait` types - such types *might* have a
22/// trailing slice type, but the type system isn't aware of it).
23///
24/// Note that `DstLayout` does not have any internal invariants, so no guarantee
25/// is made that a `DstLayout` conforms to any of Rust's requirements regarding
26/// the layout of real Rust types or instances of types.
27#[doc(hidden)]
28#[allow(missing_debug_implementations, missing_copy_implementations)]
29#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))]
30pub struct DstLayout {
31    pub(crate) align: NonZeroUsize,
32    pub(crate) size_info: SizeInfo,
33}
34
35#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
36#[derive(Copy, Clone)]
37pub(crate) enum SizeInfo<E = usize> {
38    Sized { size: usize },
39    SliceDst(TrailingSliceLayout<E>),
40}
41
42#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
43#[derive(Copy, Clone)]
44pub(crate) struct TrailingSliceLayout<E = usize> {
45    // The offset of the first byte of the trailing slice field. Note that this
46    // is NOT the same as the minimum size of the type. For example, consider
47    // the following type:
48    //
49    //   struct Foo {
50    //       a: u16,
51    //       b: u8,
52    //       c: [u8],
53    //   }
54    //
55    // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed
56    // by a padding byte.
57    pub(crate) offset: usize,
58    // The size of the element type of the trailing slice field.
59    pub(crate) elem_size: E,
60}
61
62impl SizeInfo {
63    /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a
64    /// `NonZeroUsize`. If `elem_size` is 0, returns `None`.
65    #[allow(unused)]
66    const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> {
67        Some(match *self {
68            SizeInfo::Sized { size } => SizeInfo::Sized { size },
69            SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
70                if let Some(elem_size) = NonZeroUsize::new(elem_size) {
71                    SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
72                } else {
73                    return None;
74                }
75            }
76        })
77    }
78}
79
80#[doc(hidden)]
81#[derive(Copy, Clone)]
82#[cfg_attr(test, derive(Debug))]
83#[allow(missing_debug_implementations)]
84pub enum CastType {
85    Prefix,
86    Suffix,
87}
88
89#[cfg_attr(test, derive(Debug))]
90pub(crate) enum MetadataCastError {
91    Alignment,
92    Size,
93}
94
95impl DstLayout {
96    /// The minimum possible alignment of a type.
97    const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) {
98        Some(min_align) => min_align,
99        None => const_unreachable!(),
100    };
101
102    /// The maximum theoretic possible alignment of a type.
103    ///
104    /// For compatibility with future Rust versions, this is defined as the
105    /// maximum power-of-two that fits into a `usize`. See also
106    /// [`DstLayout::CURRENT_MAX_ALIGN`].
107    pub(crate) const THEORETICAL_MAX_ALIGN: NonZeroUsize =
108        match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) {
109            Some(max_align) => max_align,
110            None => const_unreachable!(),
111        };
112
113    /// The current, documented max alignment of a type \[1\].
114    ///
115    /// \[1\] Per <https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers>:
116    ///
117    ///   The alignment value must be a power of two from 1 up to
118    ///   2<sup>29</sup>.
119    #[cfg(not(kani))]
120    #[cfg(not(target_pointer_width = "16"))]
121    pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) {
122        Some(max_align) => max_align,
123        None => const_unreachable!(),
124    };
125
126    #[cfg(not(kani))]
127    #[cfg(target_pointer_width = "16")]
128    pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 15) {
129        Some(max_align) => max_align,
130        None => const_unreachable!(),
131    };
132
133    /// Constructs a `DstLayout` for a zero-sized type with `repr_align`
134    /// alignment (or 1). If `repr_align` is provided, then it must be a power
135    /// of two.
136    ///
137    /// # Panics
138    ///
139    /// This function panics if the supplied `repr_align` is not a power of two.
140    ///
141    /// # Safety
142    ///
143    /// Unsafe code may assume that the contract of this function is satisfied.
144    #[doc(hidden)]
145    #[must_use]
146    #[inline]
147    pub const fn new_zst(repr_align: Option<NonZeroUsize>) -> DstLayout {
148        let align = match repr_align {
149            Some(align) => align,
150            None => Self::MIN_ALIGN,
151        };
152
153        const_assert!(align.get().is_power_of_two());
154
155        DstLayout { align, size_info: SizeInfo::Sized { size: 0 } }
156    }
157
158    /// Constructs a `DstLayout` which describes `T`.
159    ///
160    /// # Safety
161    ///
162    /// Unsafe code may assume that `DstLayout` is the correct layout for `T`.
163    #[doc(hidden)]
164    #[must_use]
165    #[inline]
166    pub const fn for_type<T>() -> DstLayout {
167        // SAFETY: `align` is correct by construction. `T: Sized`, and so it is
168        // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the
169        // `size` field is also correct by construction.
170        DstLayout {
171            align: match NonZeroUsize::new(mem::align_of::<T>()) {
172                Some(align) => align,
173                None => const_unreachable!(),
174            },
175            size_info: SizeInfo::Sized { size: mem::size_of::<T>() },
176        }
177    }
178
179    /// Constructs a `DstLayout` which describes `[T]`.
180    ///
181    /// # Safety
182    ///
183    /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`.
184    pub(crate) const fn for_slice<T>() -> DstLayout {
185        // SAFETY: The alignment of a slice is equal to the alignment of its
186        // element type, and so `align` is initialized correctly.
187        //
188        // Since this is just a slice type, there is no offset between the
189        // beginning of the type and the beginning of the slice, so it is
190        // correct to set `offset: 0`. The `elem_size` is correct by
191        // construction. Since `[T]` is a (degenerate case of a) slice DST, it
192        // is correct to initialize `size_info` to `SizeInfo::SliceDst`.
193        DstLayout {
194            align: match NonZeroUsize::new(mem::align_of::<T>()) {
195                Some(align) => align,
196                None => const_unreachable!(),
197            },
198            size_info: SizeInfo::SliceDst(TrailingSliceLayout {
199                offset: 0,
200                elem_size: mem::size_of::<T>(),
201            }),
202        }
203    }
204
205    /// Like `Layout::extend`, this creates a layout that describes a record
206    /// whose layout consists of `self` followed by `next` that includes the
207    /// necessary inter-field padding, but not any trailing padding.
208    ///
209    /// In order to match the layout of a `#[repr(C)]` struct, this method
210    /// should be invoked for each field in declaration order. To add trailing
211    /// padding, call `DstLayout::pad_to_align` after extending the layout for
212    /// all fields. If `self` corresponds to a type marked with
213    /// `repr(packed(N))`, then `repr_packed` should be set to `Some(N)`,
214    /// otherwise `None`.
215    ///
216    /// This method cannot be used to match the layout of a record with the
217    /// default representation, as that representation is mostly unspecified.
218    ///
219    /// # Safety
220    ///
221    /// If a (potentially hypothetical) valid `repr(C)` Rust type begins with
222    /// fields whose layout are `self`, and those fields are immediately
223    /// followed by a field whose layout is `field`, then unsafe code may rely
224    /// on `self.extend(field, repr_packed)` producing a layout that correctly
225    /// encompasses those two components.
226    ///
227    /// We make no guarantees to the behavior of this method if these fragments
228    /// cannot appear in a valid Rust type (e.g., the concatenation of the
229    /// layouts would lead to a size larger than `isize::MAX`).
230    #[doc(hidden)]
231    #[must_use]
232    #[inline]
233    pub const fn extend(self, field: DstLayout, repr_packed: Option<NonZeroUsize>) -> Self {
234        use util::{max, min, padding_needed_for};
235
236        // If `repr_packed` is `None`, there are no alignment constraints, and
237        // the value can be defaulted to `THEORETICAL_MAX_ALIGN`.
238        let max_align = match repr_packed {
239            Some(max_align) => max_align,
240            None => Self::THEORETICAL_MAX_ALIGN,
241        };
242
243        const_assert!(max_align.get().is_power_of_two());
244
245        // We use Kani to prove that this method is robust to future increases
246        // in Rust's maximum allowed alignment. However, if such a change ever
247        // actually occurs, we'd like to be notified via assertion failures.
248        #[cfg(not(kani))]
249        {
250            const_debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
251            const_debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
252            if let Some(repr_packed) = repr_packed {
253                const_debug_assert!(repr_packed.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
254            }
255        }
256
257        // The field's alignment is clamped by `repr_packed` (i.e., the
258        // `repr(packed(N))` attribute, if any) [1].
259        //
260        // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
261        //
262        //   The alignments of each field, for the purpose of positioning
263        //   fields, is the smaller of the specified alignment and the alignment
264        //   of the field's type.
265        let field_align = min(field.align, max_align);
266
267        // The struct's alignment is the maximum of its previous alignment and
268        // `field_align`.
269        let align = max(self.align, field_align);
270
271        let size_info = match self.size_info {
272            // If the layout is already a DST, we panic; DSTs cannot be extended
273            // with additional fields.
274            SizeInfo::SliceDst(..) => const_panic!("Cannot extend a DST with additional fields."),
275
276            SizeInfo::Sized { size: preceding_size } => {
277                // Compute the minimum amount of inter-field padding needed to
278                // satisfy the field's alignment, and offset of the trailing
279                // field. [1]
280                //
281                // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
282                //
283                //   Inter-field padding is guaranteed to be the minimum
284                //   required in order to satisfy each field's (possibly
285                //   altered) alignment.
286                let padding = padding_needed_for(preceding_size, field_align);
287
288                // This will not panic (and is proven to not panic, with Kani)
289                // if the layout components can correspond to a leading layout
290                // fragment of a valid Rust type, but may panic otherwise (e.g.,
291                // combining or aligning the components would create a size
292                // exceeding `isize::MAX`).
293                let offset = match preceding_size.checked_add(padding) {
294                    Some(offset) => offset,
295                    None => const_panic!("Adding padding to `self`'s size overflows `usize`."),
296                };
297
298                match field.size_info {
299                    SizeInfo::Sized { size: field_size } => {
300                        // If the trailing field is sized, the resulting layout
301                        // will be sized. Its size will be the sum of the
302                        // preceding layout, the size of the new field, and the
303                        // size of inter-field padding between the two.
304                        //
305                        // This will not panic (and is proven with Kani to not
306                        // panic) if the layout components can correspond to a
307                        // leading layout fragment of a valid Rust type, but may
308                        // panic otherwise (e.g., combining or aligning the
309                        // components would create a size exceeding
310                        // `usize::MAX`).
311                        let size = match offset.checked_add(field_size) {
312                            Some(size) => size,
313                            None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
314                        };
315                        SizeInfo::Sized { size }
316                    }
317                    SizeInfo::SliceDst(TrailingSliceLayout {
318                        offset: trailing_offset,
319                        elem_size,
320                    }) => {
321                        // If the trailing field is dynamically sized, so too
322                        // will the resulting layout. The offset of the trailing
323                        // slice component is the sum of the offset of the
324                        // trailing field and the trailing slice offset within
325                        // that field.
326                        //
327                        // This will not panic (and is proven with Kani to not
328                        // panic) if the layout components can correspond to a
329                        // leading layout fragment of a valid Rust type, but may
330                        // panic otherwise (e.g., combining or aligning the
331                        // components would create a size exceeding
332                        // `usize::MAX`).
333                        let offset = match offset.checked_add(trailing_offset) {
334                            Some(offset) => offset,
335                            None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
336                        };
337                        SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
338                    }
339                }
340            }
341        };
342
343        DstLayout { align, size_info }
344    }
345
346    /// Like `Layout::pad_to_align`, this routine rounds the size of this layout
347    /// up to the nearest multiple of this type's alignment or `repr_packed`
348    /// (whichever is less). This method leaves DST layouts unchanged, since the
349    /// trailing padding of DSTs is computed at runtime.
350    ///
351    /// In order to match the layout of a `#[repr(C)]` struct, this method
352    /// should be invoked after the invocations of [`DstLayout::extend`]. If
353    /// `self` corresponds to a type marked with `repr(packed(N))`, then
354    /// `repr_packed` should be set to `Some(N)`, otherwise `None`.
355    ///
356    /// This method cannot be used to match the layout of a record with the
357    /// default representation, as that representation is mostly unspecified.
358    ///
359    /// # Safety
360    ///
361    /// If a (potentially hypothetical) valid `repr(C)` type begins with fields
362    /// whose layout are `self` followed only by zero or more bytes of trailing
363    /// padding (not included in `self`), then unsafe code may rely on
364    /// `self.pad_to_align(repr_packed)` producing a layout that correctly
365    /// encapsulates the layout of that type.
366    ///
367    /// We make no guarantees to the behavior of this method if `self` cannot
368    /// appear in a valid Rust type (e.g., because the addition of trailing
369    /// padding would lead to a size larger than `isize::MAX`).
370    #[doc(hidden)]
371    #[must_use]
372    #[inline]
373    pub const fn pad_to_align(self) -> Self {
374        use util::padding_needed_for;
375
376        let size_info = match self.size_info {
377            // For sized layouts, we add the minimum amount of trailing padding
378            // needed to satisfy alignment.
379            SizeInfo::Sized { size: unpadded_size } => {
380                let padding = padding_needed_for(unpadded_size, self.align);
381                let size = match unpadded_size.checked_add(padding) {
382                    Some(size) => size,
383                    None => const_panic!("Adding padding caused size to overflow `usize`."),
384                };
385                SizeInfo::Sized { size }
386            }
387            // For DST layouts, trailing padding depends on the length of the
388            // trailing DST and is computed at runtime. This does not alter the
389            // offset or element size of the layout, so we leave `size_info`
390            // unchanged.
391            size_info @ SizeInfo::SliceDst(_) => size_info,
392        };
393
394        DstLayout { align: self.align, size_info }
395    }
396
397    /// Validates that a cast is sound from a layout perspective.
398    ///
399    /// Validates that the size and alignment requirements of a type with the
400    /// layout described in `self` would not be violated by performing a
401    /// `cast_type` cast from a pointer with address `addr` which refers to a
402    /// memory region of size `bytes_len`.
403    ///
404    /// If the cast is valid, `validate_cast_and_convert_metadata` returns
405    /// `(elems, split_at)`. If `self` describes a dynamically-sized type, then
406    /// `elems` is the maximum number of trailing slice elements for which a
407    /// cast would be valid (for sized types, `elem` is meaningless and should
408    /// be ignored). `split_at` is the index at which to split the memory region
409    /// in order for the prefix (suffix) to contain the result of the cast, and
410    /// in order for the remaining suffix (prefix) to contain the leftover
411    /// bytes.
412    ///
413    /// There are three conditions under which a cast can fail:
414    /// - The smallest possible value for the type is larger than the provided
415    ///   memory region
416    /// - A prefix cast is requested, and `addr` does not satisfy `self`'s
417    ///   alignment requirement
418    /// - A suffix cast is requested, and `addr + bytes_len` does not satisfy
419    ///   `self`'s alignment requirement (as a consequence, since all instances
420    ///   of the type are a multiple of its alignment, no size for the type will
421    ///   result in a starting address which is properly aligned)
422    ///
423    /// # Safety
424    ///
425    /// The caller may assume that this implementation is correct, and may rely
426    /// on that assumption for the soundness of their code. In particular, the
427    /// caller may assume that, if `validate_cast_and_convert_metadata` returns
428    /// `Some((elems, split_at))`, then:
429    /// - A pointer to the type (for dynamically sized types, this includes
430    ///   `elems` as its pointer metadata) describes an object of size `size <=
431    ///   bytes_len`
432    /// - If this is a prefix cast:
433    ///   - `addr` satisfies `self`'s alignment
434    ///   - `size == split_at`
435    /// - If this is a suffix cast:
436    ///   - `split_at == bytes_len - size`
437    ///   - `addr + split_at` satisfies `self`'s alignment
438    ///
439    /// Note that this method does *not* ensure that a pointer constructed from
440    /// its return values will be a valid pointer. In particular, this method
441    /// does not reason about `isize` overflow, which is a requirement of many
442    /// Rust pointer APIs, and may at some point be determined to be a validity
443    /// invariant of pointer types themselves. This should never be a problem so
444    /// long as the arguments to this method are derived from a known-valid
445    /// pointer (e.g., one derived from a safe Rust reference), but it is
446    /// nonetheless the caller's responsibility to justify that pointer
447    /// arithmetic will not overflow based on a safety argument *other than* the
448    /// mere fact that this method returned successfully.
449    ///
450    /// # Panics
451    ///
452    /// `validate_cast_and_convert_metadata` will panic if `self` describes a
453    /// DST whose trailing slice element is zero-sized.
454    ///
455    /// If `addr + bytes_len` overflows `usize`,
456    /// `validate_cast_and_convert_metadata` may panic, or it may return
457    /// incorrect results. No guarantees are made about when
458    /// `validate_cast_and_convert_metadata` will panic. The caller should not
459    /// rely on `validate_cast_and_convert_metadata` panicking in any particular
460    /// condition, even if `debug_assertions` are enabled.
461    #[allow(unused)]
462    #[inline(always)]
463    pub(crate) const fn validate_cast_and_convert_metadata(
464        &self,
465        addr: usize,
466        bytes_len: usize,
467        cast_type: CastType,
468    ) -> Result<(usize, usize), MetadataCastError> {
469        // `debug_assert!`, but with `#[allow(clippy::arithmetic_side_effects)]`.
470        macro_rules! __const_debug_assert {
471            ($e:expr $(, $msg:expr)?) => {
472                const_debug_assert!({
473                    #[allow(clippy::arithmetic_side_effects)]
474                    let e = $e;
475                    e
476                } $(, $msg)?);
477            };
478        }
479
480        // Note that, in practice, `self` is always a compile-time constant. We
481        // do this check earlier than needed to ensure that we always panic as a
482        // result of bugs in the program (such as calling this function on an
483        // invalid type) instead of allowing this panic to be hidden if the cast
484        // would have failed anyway for runtime reasons (such as a too-small
485        // memory region).
486        //
487        // FIXME(#67): Once our MSRV is 1.65, use let-else:
488        // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
489        let size_info = match self.size_info.try_to_nonzero_elem_size() {
490            Some(size_info) => size_info,
491            None => const_panic!("attempted to cast to slice type with zero-sized element"),
492        };
493
494        // Precondition
495        __const_debug_assert!(
496            addr.checked_add(bytes_len).is_some(),
497            "`addr` + `bytes_len` > usize::MAX"
498        );
499
500        // Alignment checks go in their own block to avoid introducing variables
501        // into the top-level scope.
502        {
503            // We check alignment for `addr` (for prefix casts) or `addr +
504            // bytes_len` (for suffix casts). For a prefix cast, the correctness
505            // of this check is trivial - `addr` is the address the object will
506            // live at.
507            //
508            // For a suffix cast, we know that all valid sizes for the type are
509            // a multiple of the alignment (and by safety precondition, we know
510            // `DstLayout` may only describe valid Rust types). Thus, a
511            // validly-sized instance which lives at a validly-aligned address
512            // must also end at a validly-aligned address. Thus, if the end
513            // address for a suffix cast (`addr + bytes_len`) is not aligned,
514            // then no valid start address will be aligned either.
515            let offset = match cast_type {
516                CastType::Prefix => 0,
517                CastType::Suffix => bytes_len,
518            };
519
520            // Addition is guaranteed not to overflow because `offset <=
521            // bytes_len`, and `addr + bytes_len <= usize::MAX` is a
522            // precondition of this method. Modulus is guaranteed not to divide
523            // by 0 because `align` is non-zero.
524            #[allow(clippy::arithmetic_side_effects)]
525            if (addr + offset) % self.align.get() != 0 {
526                return Err(MetadataCastError::Alignment);
527            }
528        }
529
530        let (elems, self_bytes) = match size_info {
531            SizeInfo::Sized { size } => {
532                if size > bytes_len {
533                    return Err(MetadataCastError::Size);
534                }
535                (0, size)
536            }
537            SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
538                // Calculate the maximum number of bytes that could be consumed
539                // - any number of bytes larger than this will either not be a
540                // multiple of the alignment, or will be larger than
541                // `bytes_len`.
542                let max_total_bytes =
543                    util::round_down_to_next_multiple_of_alignment(bytes_len, self.align);
544                // Calculate the maximum number of bytes that could be consumed
545                // by the trailing slice.
546                //
547                // FIXME(#67): Once our MSRV is 1.65, use let-else:
548                // https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
549                let max_slice_and_padding_bytes = match max_total_bytes.checked_sub(offset) {
550                    Some(max) => max,
551                    // `bytes_len` too small even for 0 trailing slice elements.
552                    None => return Err(MetadataCastError::Size),
553                };
554
555                // Calculate the number of elements that fit in
556                // `max_slice_and_padding_bytes`; any remaining bytes will be
557                // considered padding.
558                //
559                // Guaranteed not to divide by zero: `elem_size` is non-zero.
560                #[allow(clippy::arithmetic_side_effects)]
561                let elems = max_slice_and_padding_bytes / elem_size.get();
562                // Guaranteed not to overflow on multiplication: `usize::MAX >=
563                // max_slice_and_padding_bytes >= (max_slice_and_padding_bytes /
564                // elem_size) * elem_size`.
565                //
566                // Guaranteed not to overflow on addition:
567                // - max_slice_and_padding_bytes == max_total_bytes - offset
568                // - elems * elem_size <= max_slice_and_padding_bytes == max_total_bytes - offset
569                // - elems * elem_size + offset <= max_total_bytes <= usize::MAX
570                #[allow(clippy::arithmetic_side_effects)]
571                let without_padding = offset + elems * elem_size.get();
572                // `self_bytes` is equal to the offset bytes plus the bytes
573                // consumed by the trailing slice plus any padding bytes
574                // required to satisfy the alignment. Note that we have computed
575                // the maximum number of trailing slice elements that could fit
576                // in `self_bytes`, so any padding is guaranteed to be less than
577                // the size of an extra element.
578                //
579                // Guaranteed not to overflow:
580                // - By previous comment: without_padding == elems * elem_size +
581                //   offset <= max_total_bytes
582                // - By construction, `max_total_bytes` is a multiple of
583                //   `self.align`.
584                // - At most, adding padding needed to round `without_padding`
585                //   up to the next multiple of the alignment will bring
586                //   `self_bytes` up to `max_total_bytes`.
587                #[allow(clippy::arithmetic_side_effects)]
588                let self_bytes =
589                    without_padding + util::padding_needed_for(without_padding, self.align);
590                (elems, self_bytes)
591            }
592        };
593
594        __const_debug_assert!(self_bytes <= bytes_len);
595
596        let split_at = match cast_type {
597            CastType::Prefix => self_bytes,
598            // Guaranteed not to underflow:
599            // - In the `Sized` branch, only returns `size` if `size <=
600            //   bytes_len`.
601            // - In the `SliceDst` branch, calculates `self_bytes <=
602            //   max_toatl_bytes`, which is upper-bounded by `bytes_len`.
603            #[allow(clippy::arithmetic_side_effects)]
604            CastType::Suffix => bytes_len - self_bytes,
605        };
606
607        Ok((elems, split_at))
608    }
609}
610
611pub(crate) use cast_from_raw::cast_from_raw;
612mod cast_from_raw {
613    use crate::{pointer::PtrInner, *};
614
615    /// Implements [`<Dst as SizeEq<Src>>::cast_from_raw`][cast_from_raw].
616    ///
617    /// # PME
618    ///
619    /// Generates a post-monomorphization error if it is not possible to satisfy
620    /// the soundness conditions of [`SizeEq::cast_from_raw`][cast_from_raw]
621    /// for `Src` and `Dst`.
622    ///
623    /// [cast_from_raw]: crate::pointer::SizeEq::cast_from_raw
624    //
625    // FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts
626    pub(crate) fn cast_from_raw<Src, Dst>(src: PtrInner<'_, Src>) -> PtrInner<'_, Dst>
627    where
628        Src: KnownLayout<PointerMetadata = usize> + ?Sized,
629        Dst: KnownLayout<PointerMetadata = usize> + ?Sized,
630    {
631        // At compile time (specifically, post-monomorphization time), we need
632        // to compute two things:
633        // - Whether, given *any* `*Src`, it is possible to construct a `*Dst`
634        //   which addresses the same number of bytes (ie, whether, for any
635        //   `Src` pointer metadata, there exists `Dst` pointer metadata that
636        //   addresses the same number of bytes)
637        // - If this is possible, any information necessary to perform the
638        //   `Src`->`Dst` metadata conversion at runtime.
639        //
640        // Assume that `Src` and `Dst` are slice DSTs, and define:
641        // - `S_OFF = Src::LAYOUT.size_info.offset`
642        // - `S_ELEM = Src::LAYOUT.size_info.elem_size`
643        // - `D_OFF = Dst::LAYOUT.size_info.offset`
644        // - `D_ELEM = Dst::LAYOUT.size_info.elem_size`
645        //
646        // We are trying to solve the following equation:
647        //
648        //   D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
649        //
650        // At runtime, we will be attempting to compute `d_meta`, given `s_meta`
651        // (a runtime value) and all other parameters (which are compile-time
652        // values). We can solve like so:
653        //
654        //   D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
655        //
656        //   d_meta * D_ELEM = S_OFF - D_OFF + s_meta * S_ELEM
657        //
658        //   d_meta = (S_OFF - D_OFF + s_meta * S_ELEM)/D_ELEM
659        //
660        // Since `d_meta` will be a `usize`, we need the right-hand side to be
661        // an integer, and this needs to hold for *any* value of `s_meta` (in
662        // order for our conversion to be infallible - ie, to not have to reject
663        // certain values of `s_meta` at runtime). This means that:
664        // - `s_meta * S_ELEM` must be a multiple of `D_ELEM`
665        // - Since this must hold for any value of `s_meta`, `S_ELEM` must be a
666        //   multiple of `D_ELEM`
667        // - `S_OFF - D_OFF` must be a multiple of `D_ELEM`
668        //
669        // Thus, let `OFFSET_DELTA_ELEMS = (S_OFF - D_OFF)/D_ELEM` and
670        // `ELEM_MULTIPLE = S_ELEM/D_ELEM`. We can rewrite the above expression
671        // as:
672        //
673        //   d_meta = (S_OFF - D_OFF + s_meta * S_ELEM)/D_ELEM
674        //
675        //   d_meta = OFFSET_DELTA_ELEMS + s_meta * ELEM_MULTIPLE
676        //
677        // Thus, we just need to compute the following and confirm that they
678        // have integer solutions in order to both a) determine whether
679        // infallible `Src` -> `Dst` casts are possible and, b) pre-compute the
680        // parameters necessary to perform those casts at runtime. These
681        // parameters are encapsulated in `CastParams`, which acts as a witness
682        // that such infallible casts are possible.
683
684        /// The parameters required in order to perform a pointer cast from
685        /// `Src` to `Dst` as described above.
686        ///
687        /// These are a compile-time function of the layouts of `Src` and `Dst`.
688        ///
689        /// # Safety
690        ///
691        /// `offset_delta_elems` and `elem_multiple` must be valid as described
692        /// above.
693        ///
694        /// `Src`'s alignment must not be smaller than `Dst`'s alignment.
695        #[derive(Copy, Clone)]
696        struct CastParams {
697            offset_delta_elems: usize,
698            elem_multiple: usize,
699        }
700
701        impl CastParams {
702            const fn try_compute(src: &DstLayout, dst: &DstLayout) -> Option<CastParams> {
703                if src.align.get() < dst.align.get() {
704                    return None;
705                }
706
707                let (src, dst) = if let (SizeInfo::SliceDst(src), SizeInfo::SliceDst(dst)) =
708                    (src.size_info, dst.size_info)
709                {
710                    (src, dst)
711                } else {
712                    return None;
713                };
714
715                let offset_delta = if let Some(od) = src.offset.checked_sub(dst.offset) {
716                    od
717                } else {
718                    return None;
719                };
720
721                let dst_elem_size = if let Some(e) = NonZeroUsize::new(dst.elem_size) {
722                    e
723                } else {
724                    return None;
725                };
726
727                // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
728                #[allow(clippy::arithmetic_side_effects)]
729                let delta_mod_other_elem = offset_delta % dst_elem_size.get();
730
731                // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
732                #[allow(clippy::arithmetic_side_effects)]
733                let elem_remainder = src.elem_size % dst_elem_size.get();
734
735                if delta_mod_other_elem != 0 || src.elem_size < dst.elem_size || elem_remainder != 0
736                {
737                    return None;
738                }
739
740                // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
741                #[allow(clippy::arithmetic_side_effects)]
742                let offset_delta_elems = offset_delta / dst_elem_size.get();
743
744                // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
745                #[allow(clippy::arithmetic_side_effects)]
746                let elem_multiple = src.elem_size / dst_elem_size.get();
747
748                // SAFETY: We checked above that `src.align >= dst.align`.
749                Some(CastParams {
750                    // SAFETY: We checked above that this is an exact ratio.
751                    offset_delta_elems,
752                    // SAFETY: We checked above that this is an exact ratio.
753                    elem_multiple,
754                })
755            }
756
757            /// # Safety
758            ///
759            /// `src_meta` describes a `Src` whose size is no larger than
760            /// `isize::MAX`.
761            ///
762            /// The returned metadata describes a `Dst` of the same size as the
763            /// original `Src`.
764            unsafe fn cast_metadata(self, src_meta: usize) -> usize {
765                #[allow(unused)]
766                use crate::util::polyfills::*;
767
768                // SAFETY: `self` is a witness that the following equation
769                // holds:
770                //
771                //   D_OFF + d_meta * D_ELEM = S_OFF + s_meta * S_ELEM
772                //
773                // Since the caller promises that `src_meta` is valid `Src`
774                // metadata, this math will not overflow, and the returned value
775                // will describe a `Dst` of the same size.
776                #[allow(unstable_name_collisions)]
777                unsafe {
778                    self.offset_delta_elems
779                        .unchecked_add(src_meta.unchecked_mul(self.elem_multiple))
780                }
781            }
782        }
783
784        trait Params<Src: ?Sized> {
785            const CAST_PARAMS: CastParams;
786        }
787
788        impl<Src, Dst> Params<Src> for Dst
789        where
790            Src: KnownLayout + ?Sized,
791            Dst: KnownLayout<PointerMetadata = usize> + ?Sized,
792        {
793            const CAST_PARAMS: CastParams =
794                match CastParams::try_compute(&Src::LAYOUT, &Dst::LAYOUT) {
795                    Some(params) => params,
796                    None => const_panic!(
797                        "cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
798                    ),
799                };
800        }
801
802        let src_meta = <Src as KnownLayout>::pointer_to_metadata(src.as_non_null().as_ptr());
803        let params = <Dst as Params<Src>>::CAST_PARAMS;
804
805        // SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s
806        // referent is no larger than `isize::MAX`.
807        let dst_meta = unsafe { params.cast_metadata(src_meta) };
808
809        let dst = <Dst as KnownLayout>::raw_from_ptr_len(src.as_non_null().cast(), dst_meta);
810
811        // SAFETY: By post-condition on `params.cast_metadata`, `dst` addresses
812        // the same number of bytes as `src`. Since `src: PtrInner`, `src` has
813        // provenance for its entire referent, which lives inside of a single
814        // allocation. Since `dst` has the same address as `src` and was
815        // constructed using provenance-preserving operations, it addresses a
816        // subset of those bytes, and has provenance for those bytes.
817        unsafe { PtrInner::new(dst) }
818    }
819}
820
821// FIXME(#67): For some reason, on our MSRV toolchain, this `allow` isn't
822// enforced despite having `#![allow(unknown_lints)]` at the crate root, but
823// putting it here works. Once our MSRV is high enough that this bug has been
824// fixed, remove this `allow`.
825#[allow(unknown_lints)]
826#[cfg(test)]
827mod tests {
828    use super::*;
829
830    /// Tests of when a sized `DstLayout` is extended with a sized field.
831    #[allow(clippy::decimal_literal_representation)]
832    #[test]
833    fn test_dst_layout_extend_sized_with_sized() {
834        // This macro constructs a layout corresponding to a `u8` and extends it
835        // with a zero-sized trailing field of given alignment `n`. The macro
836        // tests that the resulting layout has both size and alignment `min(n,
837        // P)` for all valid values of `repr(packed(P))`.
838        macro_rules! test_align_is_size {
839            ($n:expr) => {
840                let base = DstLayout::for_type::<u8>();
841                let trailing_field = DstLayout::for_type::<elain::Align<$n>>();
842
843                let packs =
844                    core::iter::once(None).chain((0..29).map(|p| NonZeroUsize::new(2usize.pow(p))));
845
846                for pack in packs {
847                    let composite = base.extend(trailing_field, pack);
848                    let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN);
849                    let align = $n.min(max_align.get());
850                    assert_eq!(
851                        composite,
852                        DstLayout {
853                            align: NonZeroUsize::new(align).unwrap(),
854                            size_info: SizeInfo::Sized { size: align }
855                        }
856                    )
857                }
858            };
859        }
860
861        test_align_is_size!(1);
862        test_align_is_size!(2);
863        test_align_is_size!(4);
864        test_align_is_size!(8);
865        test_align_is_size!(16);
866        test_align_is_size!(32);
867        test_align_is_size!(64);
868        test_align_is_size!(128);
869        test_align_is_size!(256);
870        test_align_is_size!(512);
871        test_align_is_size!(1024);
872        test_align_is_size!(2048);
873        test_align_is_size!(4096);
874        test_align_is_size!(8192);
875        test_align_is_size!(16384);
876        test_align_is_size!(32768);
877        test_align_is_size!(65536);
878        test_align_is_size!(131072);
879        test_align_is_size!(262144);
880        test_align_is_size!(524288);
881        test_align_is_size!(1048576);
882        test_align_is_size!(2097152);
883        test_align_is_size!(4194304);
884        test_align_is_size!(8388608);
885        test_align_is_size!(16777216);
886        test_align_is_size!(33554432);
887        test_align_is_size!(67108864);
888        test_align_is_size!(33554432);
889        test_align_is_size!(134217728);
890        test_align_is_size!(268435456);
891    }
892
893    /// Tests of when a sized `DstLayout` is extended with a DST field.
894    #[test]
895    fn test_dst_layout_extend_sized_with_dst() {
896        // Test that for all combinations of real-world alignments and
897        // `repr_packed` values, that the extension of a sized `DstLayout`` with
898        // a DST field correctly computes the trailing offset in the composite
899        // layout.
900
901        let aligns = (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap());
902        let packs = core::iter::once(None).chain(aligns.clone().map(Some));
903
904        for align in aligns {
905            for pack in packs.clone() {
906                let base = DstLayout::for_type::<u8>();
907                let elem_size = 42;
908                let trailing_field_offset = 11;
909
910                let trailing_field = DstLayout {
911                    align,
912                    size_info: SizeInfo::SliceDst(TrailingSliceLayout { elem_size, offset: 11 }),
913                };
914
915                let composite = base.extend(trailing_field, pack);
916
917                let max_align = pack.unwrap_or(DstLayout::CURRENT_MAX_ALIGN).get();
918
919                let align = align.get().min(max_align);
920
921                assert_eq!(
922                    composite,
923                    DstLayout {
924                        align: NonZeroUsize::new(align).unwrap(),
925                        size_info: SizeInfo::SliceDst(TrailingSliceLayout {
926                            elem_size,
927                            offset: align + trailing_field_offset,
928                        }),
929                    }
930                )
931            }
932        }
933    }
934
935    /// Tests that calling `pad_to_align` on a sized `DstLayout` adds the
936    /// expected amount of trailing padding.
937    #[test]
938    fn test_dst_layout_pad_to_align_with_sized() {
939        // For all valid alignments `align`, construct a one-byte layout aligned
940        // to `align`, call `pad_to_align`, and assert that the size of the
941        // resulting layout is equal to `align`.
942        for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) {
943            let layout = DstLayout { align, size_info: SizeInfo::Sized { size: 1 } };
944
945            assert_eq!(
946                layout.pad_to_align(),
947                DstLayout { align, size_info: SizeInfo::Sized { size: align.get() } }
948            );
949        }
950
951        // Test explicitly-provided combinations of unpadded and padded
952        // counterparts.
953
954        macro_rules! test {
955            (unpadded { size: $unpadded_size:expr, align: $unpadded_align:expr }
956                    => padded { size: $padded_size:expr, align: $padded_align:expr }) => {
957                let unpadded = DstLayout {
958                    align: NonZeroUsize::new($unpadded_align).unwrap(),
959                    size_info: SizeInfo::Sized { size: $unpadded_size },
960                };
961                let padded = unpadded.pad_to_align();
962
963                assert_eq!(
964                    padded,
965                    DstLayout {
966                        align: NonZeroUsize::new($padded_align).unwrap(),
967                        size_info: SizeInfo::Sized { size: $padded_size },
968                    }
969                );
970            };
971        }
972
973        test!(unpadded { size: 0, align: 4 } => padded { size: 0, align: 4 });
974        test!(unpadded { size: 1, align: 4 } => padded { size: 4, align: 4 });
975        test!(unpadded { size: 2, align: 4 } => padded { size: 4, align: 4 });
976        test!(unpadded { size: 3, align: 4 } => padded { size: 4, align: 4 });
977        test!(unpadded { size: 4, align: 4 } => padded { size: 4, align: 4 });
978        test!(unpadded { size: 5, align: 4 } => padded { size: 8, align: 4 });
979        test!(unpadded { size: 6, align: 4 } => padded { size: 8, align: 4 });
980        test!(unpadded { size: 7, align: 4 } => padded { size: 8, align: 4 });
981        test!(unpadded { size: 8, align: 4 } => padded { size: 8, align: 4 });
982
983        let current_max_align = DstLayout::CURRENT_MAX_ALIGN.get();
984
985        test!(unpadded { size: 1, align: current_max_align }
986                => padded { size: current_max_align, align: current_max_align });
987
988        test!(unpadded { size: current_max_align + 1, align: current_max_align }
989                => padded { size: current_max_align * 2, align: current_max_align });
990    }
991
992    /// Tests that calling `pad_to_align` on a DST `DstLayout` is a no-op.
993    #[test]
994    fn test_dst_layout_pad_to_align_with_dst() {
995        for align in (0..29).map(|p| NonZeroUsize::new(2usize.pow(p)).unwrap()) {
996            for offset in 0..10 {
997                for elem_size in 0..10 {
998                    let layout = DstLayout {
999                        align,
1000                        size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }),
1001                    };
1002                    assert_eq!(layout.pad_to_align(), layout);
1003                }
1004            }
1005        }
1006    }
1007
1008    // This test takes a long time when running under Miri, so we skip it in
1009    // that case. This is acceptable because this is a logic test that doesn't
1010    // attempt to expose UB.
1011    #[test]
1012    #[cfg_attr(miri, ignore)]
1013    fn test_validate_cast_and_convert_metadata() {
1014        #[allow(non_local_definitions)]
1015        impl From<usize> for SizeInfo {
1016            fn from(size: usize) -> SizeInfo {
1017                SizeInfo::Sized { size }
1018            }
1019        }
1020
1021        #[allow(non_local_definitions)]
1022        impl From<(usize, usize)> for SizeInfo {
1023            fn from((offset, elem_size): (usize, usize)) -> SizeInfo {
1024                SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size })
1025            }
1026        }
1027
1028        fn layout<S: Into<SizeInfo>>(s: S, align: usize) -> DstLayout {
1029            DstLayout { size_info: s.into(), align: NonZeroUsize::new(align).unwrap() }
1030        }
1031
1032        /// This macro accepts arguments in the form of:
1033        ///
1034        ///           layout(_, _).validate(_, _, _), Ok(Some((_, _)))
1035        ///                  |  |           |  |  |            |  |
1036        ///    size ---------+  |           |  |  |            |  |
1037        ///    align -----------+           |  |  |            |  |
1038        ///    addr ------------------------+  |  |            |  |
1039        ///    bytes_len ----------------------+  |            |  |
1040        ///    cast_type -------------------------+            |  |
1041        ///    elems ------------------------------------------+  |
1042        ///    split_at ------------------------------------------+
1043        ///
1044        /// `.validate` is shorthand for `.validate_cast_and_convert_metadata`
1045        /// for brevity.
1046        ///
1047        /// Each argument can either be an iterator or a wildcard. Each
1048        /// wildcarded variable is implicitly replaced by an iterator over a
1049        /// representative sample of values for that variable. Each `test!`
1050        /// invocation iterates over every combination of values provided by
1051        /// each variable's iterator (ie, the cartesian product) and validates
1052        /// that the results are expected.
1053        ///
1054        /// The final argument uses the same syntax, but it has a different
1055        /// meaning:
1056        /// - If it is `Ok(pat)`, then the pattern `pat` is supplied to
1057        ///   a matching assert to validate the computed result for each
1058        ///   combination of input values.
1059        /// - If it is `Err(Some(msg) | None)`, then `test!` validates that the
1060        ///   call to `validate_cast_and_convert_metadata` panics with the given
1061        ///   panic message or, if the current Rust toolchain version is too
1062        ///   early to support panicking in `const fn`s, panics with *some*
1063        ///   message. In the latter case, the `const_panic!` macro is used,
1064        ///   which emits code which causes a non-panicking error at const eval
1065        ///   time, but which does panic when invoked at runtime. Thus, it is
1066        ///   merely difficult to predict the *value* of this panic. We deem
1067        ///   that testing against the real panic strings on stable and nightly
1068        ///   toolchains is enough to ensure correctness.
1069        ///
1070        /// Note that the meta-variables that match these variables have the
1071        /// `tt` type, and some valid expressions are not valid `tt`s (such as
1072        /// `a..b`). In this case, wrap the expression in parentheses, and it
1073        /// will become valid `tt`.
1074        macro_rules! test {
1075                (
1076                    layout($size:tt, $align:tt)
1077                    .validate($addr:tt, $bytes_len:tt, $cast_type:tt), $expect:pat $(,)?
1078                ) => {
1079                    itertools::iproduct!(
1080                        test!(@generate_size $size),
1081                        test!(@generate_align $align),
1082                        test!(@generate_usize $addr),
1083                        test!(@generate_usize $bytes_len),
1084                        test!(@generate_cast_type $cast_type)
1085                    ).for_each(|(size_info, align, addr, bytes_len, cast_type)| {
1086                        // Temporarily disable the panic hook installed by the test
1087                        // harness. If we don't do this, all panic messages will be
1088                        // kept in an internal log. On its own, this isn't a
1089                        // problem, but if a non-caught panic ever happens (ie, in
1090                        // code later in this test not in this macro), all of the
1091                        // previously-buffered messages will be dumped, hiding the
1092                        // real culprit.
1093                        let previous_hook = std::panic::take_hook();
1094                        // I don't understand why, but this seems to be required in
1095                        // addition to the previous line.
1096                        std::panic::set_hook(Box::new(|_| {}));
1097                        let actual = std::panic::catch_unwind(|| {
1098                            layout(size_info, align).validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
1099                        }).map_err(|d| {
1100                            let msg = d.downcast::<&'static str>().ok().map(|s| *s.as_ref());
1101                            assert!(msg.is_some() || cfg!(not(zerocopy_panic_in_const_and_vec_try_reserve_1_57_0)), "non-string panic messages are not permitted when `--cfg zerocopy_panic_in_const_and_vec_try_reserve` is set");
1102                            msg
1103                        });
1104                        std::panic::set_hook(previous_hook);
1105
1106                        assert!(
1107                            matches!(actual, $expect),
1108                            "layout({:?}, {}).validate_cast_and_convert_metadata({}, {}, {:?})" ,size_info, align, addr, bytes_len, cast_type
1109                        );
1110                    });
1111                };
1112                (@generate_usize _) => { 0..8 };
1113                // Generate sizes for both Sized and !Sized types.
1114                (@generate_size _) => {
1115                    test!(@generate_size (_)).chain(test!(@generate_size (_, _)))
1116                };
1117                // Generate sizes for both Sized and !Sized types by chaining
1118                // specified iterators for each.
1119                (@generate_size ($sized_sizes:tt | $unsized_sizes:tt)) => {
1120                    test!(@generate_size ($sized_sizes)).chain(test!(@generate_size $unsized_sizes))
1121                };
1122                // Generate sizes for Sized types.
1123                (@generate_size (_)) => { test!(@generate_size (0..8)) };
1124                (@generate_size ($sizes:expr)) => { $sizes.into_iter().map(Into::<SizeInfo>::into) };
1125                // Generate sizes for !Sized types.
1126                (@generate_size ($min_sizes:tt, $elem_sizes:tt)) => {
1127                    itertools::iproduct!(
1128                        test!(@generate_min_size $min_sizes),
1129                        test!(@generate_elem_size $elem_sizes)
1130                    ).map(Into::<SizeInfo>::into)
1131                };
1132                (@generate_fixed_size _) => { (0..8).into_iter().map(Into::<SizeInfo>::into) };
1133                (@generate_min_size _) => { 0..8 };
1134                (@generate_elem_size _) => { 1..8 };
1135                (@generate_align _) => { [1, 2, 4, 8, 16] };
1136                (@generate_opt_usize _) => { [None].into_iter().chain((0..8).map(Some).into_iter()) };
1137                (@generate_cast_type _) => { [CastType::Prefix, CastType::Suffix] };
1138                (@generate_cast_type $variant:ident) => { [CastType::$variant] };
1139                // Some expressions need to be wrapped in parentheses in order to be
1140                // valid `tt`s (required by the top match pattern). See the comment
1141                // below for more details. This arm removes these parentheses to
1142                // avoid generating an `unused_parens` warning.
1143                (@$_:ident ($vals:expr)) => { $vals };
1144                (@$_:ident $vals:expr) => { $vals };
1145            }
1146
1147        const EVENS: [usize; 8] = [0, 2, 4, 6, 8, 10, 12, 14];
1148        const ODDS: [usize; 8] = [1, 3, 5, 7, 9, 11, 13, 15];
1149
1150        // base_size is too big for the memory region.
1151        test!(
1152            layout(((1..8) | ((1..8), (1..8))), _).validate([0], [0], _),
1153            Ok(Err(MetadataCastError::Size))
1154        );
1155        test!(
1156            layout(((2..8) | ((2..8), (2..8))), _).validate([0], [1], Prefix),
1157            Ok(Err(MetadataCastError::Size))
1158        );
1159        test!(
1160            layout(((2..8) | ((2..8), (2..8))), _).validate([0x1000_0000 - 1], [1], Suffix),
1161            Ok(Err(MetadataCastError::Size))
1162        );
1163
1164        // addr is unaligned for prefix cast
1165        test!(layout(_, [2]).validate(ODDS, _, Prefix), Ok(Err(MetadataCastError::Alignment)));
1166        test!(layout(_, [2]).validate(ODDS, _, Prefix), Ok(Err(MetadataCastError::Alignment)));
1167
1168        // addr is aligned, but end of buffer is unaligned for suffix cast
1169        test!(layout(_, [2]).validate(EVENS, ODDS, Suffix), Ok(Err(MetadataCastError::Alignment)));
1170        test!(layout(_, [2]).validate(EVENS, ODDS, Suffix), Ok(Err(MetadataCastError::Alignment)));
1171
1172        // Unfortunately, these constants cannot easily be used in the
1173        // implementation of `validate_cast_and_convert_metadata`, since
1174        // `panic!` consumes a string literal, not an expression.
1175        //
1176        // It's important that these messages be in a separate module. If they
1177        // were at the function's top level, we'd pass them to `test!` as, e.g.,
1178        // `Err(TRAILING)`, which would run into a subtle Rust footgun - the
1179        // `TRAILING` identifier would be treated as a pattern to match rather
1180        // than a value to check for equality.
1181        mod msgs {
1182            pub(super) const TRAILING: &str =
1183                "attempted to cast to slice type with zero-sized element";
1184            pub(super) const OVERFLOW: &str = "`addr` + `bytes_len` > usize::MAX";
1185        }
1186
1187        // casts with ZST trailing element types are unsupported
1188        test!(layout((_, [0]), _).validate(_, _, _), Err(Some(msgs::TRAILING) | None),);
1189
1190        // addr + bytes_len must not overflow usize
1191        test!(layout(_, _).validate([usize::MAX], (1..100), _), Err(Some(msgs::OVERFLOW) | None));
1192        test!(layout(_, _).validate((1..100), [usize::MAX], _), Err(Some(msgs::OVERFLOW) | None));
1193        test!(
1194            layout(_, _).validate(
1195                [usize::MAX / 2 + 1, usize::MAX],
1196                [usize::MAX / 2 + 1, usize::MAX],
1197                _
1198            ),
1199            Err(Some(msgs::OVERFLOW) | None)
1200        );
1201
1202        // Validates that `validate_cast_and_convert_metadata` satisfies its own
1203        // documented safety postconditions, and also a few other properties
1204        // that aren't documented but we want to guarantee anyway.
1205        fn validate_behavior(
1206            (layout, addr, bytes_len, cast_type): (DstLayout, usize, usize, CastType),
1207        ) {
1208            if let Ok((elems, split_at)) =
1209                layout.validate_cast_and_convert_metadata(addr, bytes_len, cast_type)
1210            {
1211                let (size_info, align) = (layout.size_info, layout.align);
1212                let debug_str = format!(
1213                    "layout({:?}, {}).validate_cast_and_convert_metadata({}, {}, {:?}) => ({}, {})",
1214                    size_info, align, addr, bytes_len, cast_type, elems, split_at
1215                );
1216
1217                // If this is a sized type (no trailing slice), then `elems` is
1218                // meaningless, but in practice we set it to 0. Callers are not
1219                // allowed to rely on this, but a lot of math is nicer if
1220                // they're able to, and some callers might accidentally do that.
1221                let sized = matches!(layout.size_info, SizeInfo::Sized { .. });
1222                assert!(!(sized && elems != 0), "{}", debug_str);
1223
1224                let resulting_size = match layout.size_info {
1225                    SizeInfo::Sized { size } => size,
1226                    SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
1227                        let padded_size = |elems| {
1228                            let without_padding = offset + elems * elem_size;
1229                            without_padding + util::padding_needed_for(without_padding, align)
1230                        };
1231
1232                        let resulting_size = padded_size(elems);
1233                        // Test that `validate_cast_and_convert_metadata`
1234                        // computed the largest possible value that fits in the
1235                        // given range.
1236                        assert!(padded_size(elems + 1) > bytes_len, "{}", debug_str);
1237                        resulting_size
1238                    }
1239                };
1240
1241                // Test safety postconditions guaranteed by
1242                // `validate_cast_and_convert_metadata`.
1243                assert!(resulting_size <= bytes_len, "{}", debug_str);
1244                match cast_type {
1245                    CastType::Prefix => {
1246                        assert_eq!(addr % align, 0, "{}", debug_str);
1247                        assert_eq!(resulting_size, split_at, "{}", debug_str);
1248                    }
1249                    CastType::Suffix => {
1250                        assert_eq!(split_at, bytes_len - resulting_size, "{}", debug_str);
1251                        assert_eq!((addr + split_at) % align, 0, "{}", debug_str);
1252                    }
1253                }
1254            } else {
1255                let min_size = match layout.size_info {
1256                    SizeInfo::Sized { size } => size,
1257                    SizeInfo::SliceDst(TrailingSliceLayout { offset, .. }) => {
1258                        offset + util::padding_needed_for(offset, layout.align)
1259                    }
1260                };
1261
1262                // If a cast is invalid, it is either because...
1263                // 1. there are insufficient bytes at the given region for type:
1264                let insufficient_bytes = bytes_len < min_size;
1265                // 2. performing the cast would misalign type:
1266                let base = match cast_type {
1267                    CastType::Prefix => 0,
1268                    CastType::Suffix => bytes_len,
1269                };
1270                let misaligned = (base + addr) % layout.align != 0;
1271
1272                assert!(insufficient_bytes || misaligned);
1273            }
1274        }
1275
1276        let sizes = 0..8;
1277        let elem_sizes = 1..8;
1278        let size_infos = sizes
1279            .clone()
1280            .map(Into::<SizeInfo>::into)
1281            .chain(itertools::iproduct!(sizes, elem_sizes).map(Into::<SizeInfo>::into));
1282        let layouts = itertools::iproduct!(size_infos, [1, 2, 4, 8, 16, 32])
1283                .filter(|(size_info, align)| !matches!(size_info, SizeInfo::Sized { size } if size % align != 0))
1284                .map(|(size_info, align)| layout(size_info, align));
1285        itertools::iproduct!(layouts, 0..8, 0..8, [CastType::Prefix, CastType::Suffix])
1286            .for_each(validate_behavior);
1287    }
1288
1289    #[test]
1290    #[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
1291    fn test_validate_rust_layout() {
1292        use core::{
1293            convert::TryInto as _,
1294            ptr::{self, NonNull},
1295        };
1296
1297        use crate::util::testutil::*;
1298
1299        // This test synthesizes pointers with various metadata and uses Rust's
1300        // built-in APIs to confirm that Rust makes decisions about type layout
1301        // which are consistent with what we believe is guaranteed by the
1302        // language. If this test fails, it doesn't just mean our code is wrong
1303        // - it means we're misunderstanding the language's guarantees.
1304
1305        #[derive(Debug)]
1306        struct MacroArgs {
1307            offset: usize,
1308            align: NonZeroUsize,
1309            elem_size: Option<usize>,
1310        }
1311
1312        /// # Safety
1313        ///
1314        /// `test` promises to only call `addr_of_slice_field` on a `NonNull<T>`
1315        /// which points to a valid `T`.
1316        ///
1317        /// `with_elems` must produce a pointer which points to a valid `T`.
1318        fn test<T: ?Sized, W: Fn(usize) -> NonNull<T>>(
1319            args: MacroArgs,
1320            with_elems: W,
1321            addr_of_slice_field: Option<fn(NonNull<T>) -> NonNull<u8>>,
1322        ) {
1323            let dst = args.elem_size.is_some();
1324            let layout = {
1325                let size_info = match args.elem_size {
1326                    Some(elem_size) => {
1327                        SizeInfo::SliceDst(TrailingSliceLayout { offset: args.offset, elem_size })
1328                    }
1329                    None => SizeInfo::Sized {
1330                        // Rust only supports types whose sizes are a multiple
1331                        // of their alignment. If the macro created a type like
1332                        // this:
1333                        //
1334                        //   #[repr(C, align(2))]
1335                        //   struct Foo([u8; 1]);
1336                        //
1337                        // ...then Rust will automatically round the type's size
1338                        // up to 2.
1339                        size: args.offset + util::padding_needed_for(args.offset, args.align),
1340                    },
1341                };
1342                DstLayout { size_info, align: args.align }
1343            };
1344
1345            for elems in 0..128 {
1346                let ptr = with_elems(elems);
1347
1348                if let Some(addr_of_slice_field) = addr_of_slice_field {
1349                    let slc_field_ptr = addr_of_slice_field(ptr).as_ptr();
1350                    // SAFETY: Both `slc_field_ptr` and `ptr` are pointers to
1351                    // the same valid Rust object.
1352                    // Work around https://github.com/rust-lang/rust-clippy/issues/12280
1353                    let offset: usize =
1354                        unsafe { slc_field_ptr.byte_offset_from(ptr.as_ptr()).try_into().unwrap() };
1355                    assert_eq!(offset, args.offset);
1356                }
1357
1358                // SAFETY: `ptr` points to a valid `T`.
1359                let (size, align) = unsafe {
1360                    (mem::size_of_val_raw(ptr.as_ptr()), mem::align_of_val_raw(ptr.as_ptr()))
1361                };
1362
1363                // Avoid expensive allocation when running under Miri.
1364                let assert_msg = if !cfg!(miri) {
1365                    format!("\n{:?}\nsize:{}, align:{}", args, size, align)
1366                } else {
1367                    String::new()
1368                };
1369
1370                let without_padding =
1371                    args.offset + args.elem_size.map(|elem_size| elems * elem_size).unwrap_or(0);
1372                assert!(size >= without_padding, "{}", assert_msg);
1373                assert_eq!(align, args.align.get(), "{}", assert_msg);
1374
1375                // This encodes the most important part of the test: our
1376                // understanding of how Rust determines the layout of repr(C)
1377                // types. Sized repr(C) types are trivial, but DST types have
1378                // some subtlety. Note that:
1379                // - For sized types, `without_padding` is just the size of the
1380                //   type that we constructed for `Foo`. Since we may have
1381                //   requested a larger alignment, `Foo` may actually be larger
1382                //   than this, hence `padding_needed_for`.
1383                // - For unsized types, `without_padding` is dynamically
1384                //   computed from the offset, the element size, and element
1385                //   count. We expect that the size of the object should be
1386                //   `offset + elem_size * elems` rounded up to the next
1387                //   alignment.
1388                let expected_size =
1389                    without_padding + util::padding_needed_for(without_padding, args.align);
1390                assert_eq!(expected_size, size, "{}", assert_msg);
1391
1392                // For zero-sized element types,
1393                // `validate_cast_and_convert_metadata` just panics, so we skip
1394                // testing those types.
1395                if args.elem_size.map(|elem_size| elem_size > 0).unwrap_or(true) {
1396                    let addr = ptr.addr().get();
1397                    let (got_elems, got_split_at) = layout
1398                        .validate_cast_and_convert_metadata(addr, size, CastType::Prefix)
1399                        .unwrap();
1400                    // Avoid expensive allocation when running under Miri.
1401                    let assert_msg = if !cfg!(miri) {
1402                        format!(
1403                            "{}\nvalidate_cast_and_convert_metadata({}, {})",
1404                            assert_msg, addr, size,
1405                        )
1406                    } else {
1407                        String::new()
1408                    };
1409                    assert_eq!(got_split_at, size, "{}", assert_msg);
1410                    if dst {
1411                        assert!(got_elems >= elems, "{}", assert_msg);
1412                        if got_elems != elems {
1413                            // If `validate_cast_and_convert_metadata`
1414                            // returned more elements than `elems`, that
1415                            // means that `elems` is not the maximum number
1416                            // of elements that can fit in `size` - in other
1417                            // words, there is enough padding at the end of
1418                            // the value to fit at least one more element.
1419                            // If we use this metadata to synthesize a
1420                            // pointer, despite having a different element
1421                            // count, we still expect it to have the same
1422                            // size.
1423                            let got_ptr = with_elems(got_elems);
1424                            // SAFETY: `got_ptr` is a pointer to a valid `T`.
1425                            let size_of_got_ptr = unsafe { mem::size_of_val_raw(got_ptr.as_ptr()) };
1426                            assert_eq!(size_of_got_ptr, size, "{}", assert_msg);
1427                        }
1428                    } else {
1429                        // For sized casts, the returned element value is
1430                        // technically meaningless, and we don't guarantee any
1431                        // particular value. In practice, it's always zero.
1432                        assert_eq!(got_elems, 0, "{}", assert_msg)
1433                    }
1434                }
1435            }
1436        }
1437
1438        macro_rules! validate_against_rust {
1439                ($offset:literal, $align:literal $(, $elem_size:literal)?) => {{
1440                    #[repr(C, align($align))]
1441                    struct Foo([u8; $offset]$(, [[u8; $elem_size]])?);
1442
1443                    let args = MacroArgs {
1444                        offset: $offset,
1445                        align: $align.try_into().unwrap(),
1446                        elem_size: {
1447                            #[allow(unused)]
1448                            let ret = None::<usize>;
1449                            $(let ret = Some($elem_size);)?
1450                            ret
1451                        }
1452                    };
1453
1454                    #[repr(C, align($align))]
1455                    struct FooAlign;
1456                    // Create an aligned buffer to use in order to synthesize
1457                    // pointers to `Foo`. We don't ever load values from these
1458                    // pointers - we just do arithmetic on them - so having a "real"
1459                    // block of memory as opposed to a validly-aligned-but-dangling
1460                    // pointer is only necessary to make Miri happy since we run it
1461                    // with "strict provenance" checking enabled.
1462                    let aligned_buf = Align::<_, FooAlign>::new([0u8; 1024]);
1463                    let with_elems = |elems| {
1464                        let slc = NonNull::slice_from_raw_parts(NonNull::from(&aligned_buf.t), elems);
1465                        #[allow(clippy::as_conversions)]
1466                        NonNull::new(slc.as_ptr() as *mut Foo).unwrap()
1467                    };
1468                    let addr_of_slice_field = {
1469                        #[allow(unused)]
1470                        let f = None::<fn(NonNull<Foo>) -> NonNull<u8>>;
1471                        $(
1472                            // SAFETY: `test` promises to only call `f` with a `ptr`
1473                            // to a valid `Foo`.
1474                            let f: Option<fn(NonNull<Foo>) -> NonNull<u8>> = Some(|ptr: NonNull<Foo>| unsafe {
1475                                NonNull::new(ptr::addr_of_mut!((*ptr.as_ptr()).1)).unwrap().cast::<u8>()
1476                            });
1477                            let _ = $elem_size;
1478                        )?
1479                        f
1480                    };
1481
1482                    test::<Foo, _>(args, with_elems, addr_of_slice_field);
1483                }};
1484            }
1485
1486        // Every permutation of:
1487        // - offset in [0, 4]
1488        // - align in [1, 16]
1489        // - elem_size in [0, 4] (plus no elem_size)
1490        validate_against_rust!(0, 1);
1491        validate_against_rust!(0, 1, 0);
1492        validate_against_rust!(0, 1, 1);
1493        validate_against_rust!(0, 1, 2);
1494        validate_against_rust!(0, 1, 3);
1495        validate_against_rust!(0, 1, 4);
1496        validate_against_rust!(0, 2);
1497        validate_against_rust!(0, 2, 0);
1498        validate_against_rust!(0, 2, 1);
1499        validate_against_rust!(0, 2, 2);
1500        validate_against_rust!(0, 2, 3);
1501        validate_against_rust!(0, 2, 4);
1502        validate_against_rust!(0, 4);
1503        validate_against_rust!(0, 4, 0);
1504        validate_against_rust!(0, 4, 1);
1505        validate_against_rust!(0, 4, 2);
1506        validate_against_rust!(0, 4, 3);
1507        validate_against_rust!(0, 4, 4);
1508        validate_against_rust!(0, 8);
1509        validate_against_rust!(0, 8, 0);
1510        validate_against_rust!(0, 8, 1);
1511        validate_against_rust!(0, 8, 2);
1512        validate_against_rust!(0, 8, 3);
1513        validate_against_rust!(0, 8, 4);
1514        validate_against_rust!(0, 16);
1515        validate_against_rust!(0, 16, 0);
1516        validate_against_rust!(0, 16, 1);
1517        validate_against_rust!(0, 16, 2);
1518        validate_against_rust!(0, 16, 3);
1519        validate_against_rust!(0, 16, 4);
1520        validate_against_rust!(1, 1);
1521        validate_against_rust!(1, 1, 0);
1522        validate_against_rust!(1, 1, 1);
1523        validate_against_rust!(1, 1, 2);
1524        validate_against_rust!(1, 1, 3);
1525        validate_against_rust!(1, 1, 4);
1526        validate_against_rust!(1, 2);
1527        validate_against_rust!(1, 2, 0);
1528        validate_against_rust!(1, 2, 1);
1529        validate_against_rust!(1, 2, 2);
1530        validate_against_rust!(1, 2, 3);
1531        validate_against_rust!(1, 2, 4);
1532        validate_against_rust!(1, 4);
1533        validate_against_rust!(1, 4, 0);
1534        validate_against_rust!(1, 4, 1);
1535        validate_against_rust!(1, 4, 2);
1536        validate_against_rust!(1, 4, 3);
1537        validate_against_rust!(1, 4, 4);
1538        validate_against_rust!(1, 8);
1539        validate_against_rust!(1, 8, 0);
1540        validate_against_rust!(1, 8, 1);
1541        validate_against_rust!(1, 8, 2);
1542        validate_against_rust!(1, 8, 3);
1543        validate_against_rust!(1, 8, 4);
1544        validate_against_rust!(1, 16);
1545        validate_against_rust!(1, 16, 0);
1546        validate_against_rust!(1, 16, 1);
1547        validate_against_rust!(1, 16, 2);
1548        validate_against_rust!(1, 16, 3);
1549        validate_against_rust!(1, 16, 4);
1550        validate_against_rust!(2, 1);
1551        validate_against_rust!(2, 1, 0);
1552        validate_against_rust!(2, 1, 1);
1553        validate_against_rust!(2, 1, 2);
1554        validate_against_rust!(2, 1, 3);
1555        validate_against_rust!(2, 1, 4);
1556        validate_against_rust!(2, 2);
1557        validate_against_rust!(2, 2, 0);
1558        validate_against_rust!(2, 2, 1);
1559        validate_against_rust!(2, 2, 2);
1560        validate_against_rust!(2, 2, 3);
1561        validate_against_rust!(2, 2, 4);
1562        validate_against_rust!(2, 4);
1563        validate_against_rust!(2, 4, 0);
1564        validate_against_rust!(2, 4, 1);
1565        validate_against_rust!(2, 4, 2);
1566        validate_against_rust!(2, 4, 3);
1567        validate_against_rust!(2, 4, 4);
1568        validate_against_rust!(2, 8);
1569        validate_against_rust!(2, 8, 0);
1570        validate_against_rust!(2, 8, 1);
1571        validate_against_rust!(2, 8, 2);
1572        validate_against_rust!(2, 8, 3);
1573        validate_against_rust!(2, 8, 4);
1574        validate_against_rust!(2, 16);
1575        validate_against_rust!(2, 16, 0);
1576        validate_against_rust!(2, 16, 1);
1577        validate_against_rust!(2, 16, 2);
1578        validate_against_rust!(2, 16, 3);
1579        validate_against_rust!(2, 16, 4);
1580        validate_against_rust!(3, 1);
1581        validate_against_rust!(3, 1, 0);
1582        validate_against_rust!(3, 1, 1);
1583        validate_against_rust!(3, 1, 2);
1584        validate_against_rust!(3, 1, 3);
1585        validate_against_rust!(3, 1, 4);
1586        validate_against_rust!(3, 2);
1587        validate_against_rust!(3, 2, 0);
1588        validate_against_rust!(3, 2, 1);
1589        validate_against_rust!(3, 2, 2);
1590        validate_against_rust!(3, 2, 3);
1591        validate_against_rust!(3, 2, 4);
1592        validate_against_rust!(3, 4);
1593        validate_against_rust!(3, 4, 0);
1594        validate_against_rust!(3, 4, 1);
1595        validate_against_rust!(3, 4, 2);
1596        validate_against_rust!(3, 4, 3);
1597        validate_against_rust!(3, 4, 4);
1598        validate_against_rust!(3, 8);
1599        validate_against_rust!(3, 8, 0);
1600        validate_against_rust!(3, 8, 1);
1601        validate_against_rust!(3, 8, 2);
1602        validate_against_rust!(3, 8, 3);
1603        validate_against_rust!(3, 8, 4);
1604        validate_against_rust!(3, 16);
1605        validate_against_rust!(3, 16, 0);
1606        validate_against_rust!(3, 16, 1);
1607        validate_against_rust!(3, 16, 2);
1608        validate_against_rust!(3, 16, 3);
1609        validate_against_rust!(3, 16, 4);
1610        validate_against_rust!(4, 1);
1611        validate_against_rust!(4, 1, 0);
1612        validate_against_rust!(4, 1, 1);
1613        validate_against_rust!(4, 1, 2);
1614        validate_against_rust!(4, 1, 3);
1615        validate_against_rust!(4, 1, 4);
1616        validate_against_rust!(4, 2);
1617        validate_against_rust!(4, 2, 0);
1618        validate_against_rust!(4, 2, 1);
1619        validate_against_rust!(4, 2, 2);
1620        validate_against_rust!(4, 2, 3);
1621        validate_against_rust!(4, 2, 4);
1622        validate_against_rust!(4, 4);
1623        validate_against_rust!(4, 4, 0);
1624        validate_against_rust!(4, 4, 1);
1625        validate_against_rust!(4, 4, 2);
1626        validate_against_rust!(4, 4, 3);
1627        validate_against_rust!(4, 4, 4);
1628        validate_against_rust!(4, 8);
1629        validate_against_rust!(4, 8, 0);
1630        validate_against_rust!(4, 8, 1);
1631        validate_against_rust!(4, 8, 2);
1632        validate_against_rust!(4, 8, 3);
1633        validate_against_rust!(4, 8, 4);
1634        validate_against_rust!(4, 16);
1635        validate_against_rust!(4, 16, 0);
1636        validate_against_rust!(4, 16, 1);
1637        validate_against_rust!(4, 16, 2);
1638        validate_against_rust!(4, 16, 3);
1639        validate_against_rust!(4, 16, 4);
1640    }
1641}
1642
1643#[cfg(kani)]
1644mod proofs {
1645    use core::alloc::Layout;
1646
1647    use super::*;
1648
1649    impl kani::Arbitrary for DstLayout {
1650        fn any() -> Self {
1651            let align: NonZeroUsize = kani::any();
1652            let size_info: SizeInfo = kani::any();
1653
1654            kani::assume(align.is_power_of_two());
1655            kani::assume(align < DstLayout::THEORETICAL_MAX_ALIGN);
1656
1657            // For testing purposes, we most care about instantiations of
1658            // `DstLayout` that can correspond to actual Rust types. We use
1659            // `Layout` to verify that our `DstLayout` satisfies the validity
1660            // conditions of Rust layouts.
1661            kani::assume(
1662                match size_info {
1663                    SizeInfo::Sized { size } => Layout::from_size_align(size, align.get()),
1664                    SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size: _ }) => {
1665                        // `SliceDst` cannot encode an exact size, but we know
1666                        // it is at least `offset` bytes.
1667                        Layout::from_size_align(offset, align.get())
1668                    }
1669                }
1670                .is_ok(),
1671            );
1672
1673            Self { align: align, size_info: size_info }
1674        }
1675    }
1676
1677    impl kani::Arbitrary for SizeInfo {
1678        fn any() -> Self {
1679            let is_sized: bool = kani::any();
1680
1681            match is_sized {
1682                true => {
1683                    let size: usize = kani::any();
1684
1685                    kani::assume(size <= isize::MAX as _);
1686
1687                    SizeInfo::Sized { size }
1688                }
1689                false => SizeInfo::SliceDst(kani::any()),
1690            }
1691        }
1692    }
1693
1694    impl kani::Arbitrary for TrailingSliceLayout {
1695        fn any() -> Self {
1696            let elem_size: usize = kani::any();
1697            let offset: usize = kani::any();
1698
1699            kani::assume(elem_size < isize::MAX as _);
1700            kani::assume(offset < isize::MAX as _);
1701
1702            TrailingSliceLayout { elem_size, offset }
1703        }
1704    }
1705
1706    #[kani::proof]
1707    fn prove_dst_layout_extend() {
1708        use crate::util::{max, min, padding_needed_for};
1709
1710        let base: DstLayout = kani::any();
1711        let field: DstLayout = kani::any();
1712        let packed: Option<NonZeroUsize> = kani::any();
1713
1714        if let Some(max_align) = packed {
1715            kani::assume(max_align.is_power_of_two());
1716            kani::assume(base.align <= max_align);
1717        }
1718
1719        // The base can only be extended if it's sized.
1720        kani::assume(matches!(base.size_info, SizeInfo::Sized { .. }));
1721        let base_size = if let SizeInfo::Sized { size } = base.size_info {
1722            size
1723        } else {
1724            unreachable!();
1725        };
1726
1727        // Under the above conditions, `DstLayout::extend` will not panic.
1728        let composite = base.extend(field, packed);
1729
1730        // The field's alignment is clamped by `max_align` (i.e., the
1731        // `packed` attribute, if any) [1].
1732        //
1733        // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
1734        //
1735        //   The alignments of each field, for the purpose of positioning
1736        //   fields, is the smaller of the specified alignment and the
1737        //   alignment of the field's type.
1738        let field_align = min(field.align, packed.unwrap_or(DstLayout::THEORETICAL_MAX_ALIGN));
1739
1740        // The struct's alignment is the maximum of its previous alignment and
1741        // `field_align`.
1742        assert_eq!(composite.align, max(base.align, field_align));
1743
1744        // Compute the minimum amount of inter-field padding needed to
1745        // satisfy the field's alignment, and offset of the trailing field.
1746        // [1]
1747        //
1748        // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers:
1749        //
1750        //   Inter-field padding is guaranteed to be the minimum required in
1751        //   order to satisfy each field's (possibly altered) alignment.
1752        let padding = padding_needed_for(base_size, field_align);
1753        let offset = base_size + padding;
1754
1755        // For testing purposes, we'll also construct `alloc::Layout`
1756        // stand-ins for `DstLayout`, and show that `extend` behaves
1757        // comparably on both types.
1758        let base_analog = Layout::from_size_align(base_size, base.align.get()).unwrap();
1759
1760        match field.size_info {
1761            SizeInfo::Sized { size: field_size } => {
1762                if let SizeInfo::Sized { size: composite_size } = composite.size_info {
1763                    // If the trailing field is sized, the resulting layout will
1764                    // be sized. Its size will be the sum of the preceding
1765                    // layout, the size of the new field, and the size of
1766                    // inter-field padding between the two.
1767                    assert_eq!(composite_size, offset + field_size);
1768
1769                    let field_analog =
1770                        Layout::from_size_align(field_size, field_align.get()).unwrap();
1771
1772                    if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog)
1773                    {
1774                        assert_eq!(actual_offset, offset);
1775                        assert_eq!(actual_composite.size(), composite_size);
1776                        assert_eq!(actual_composite.align(), composite.align.get());
1777                    } else {
1778                        // An error here reflects that composite of `base`
1779                        // and `field` cannot correspond to a real Rust type
1780                        // fragment, because such a fragment would violate
1781                        // the basic invariants of a valid Rust layout. At
1782                        // the time of writing, `DstLayout` is a little more
1783                        // permissive than `Layout`, so we don't assert
1784                        // anything in this branch (e.g., unreachability).
1785                    }
1786                } else {
1787                    panic!("The composite of two sized layouts must be sized.")
1788                }
1789            }
1790            SizeInfo::SliceDst(TrailingSliceLayout {
1791                offset: field_offset,
1792                elem_size: field_elem_size,
1793            }) => {
1794                if let SizeInfo::SliceDst(TrailingSliceLayout {
1795                    offset: composite_offset,
1796                    elem_size: composite_elem_size,
1797                }) = composite.size_info
1798                {
1799                    // The offset of the trailing slice component is the sum
1800                    // of the offset of the trailing field and the trailing
1801                    // slice offset within that field.
1802                    assert_eq!(composite_offset, offset + field_offset);
1803                    // The elem size is unchanged.
1804                    assert_eq!(composite_elem_size, field_elem_size);
1805
1806                    let field_analog =
1807                        Layout::from_size_align(field_offset, field_align.get()).unwrap();
1808
1809                    if let Ok((actual_composite, actual_offset)) = base_analog.extend(field_analog)
1810                    {
1811                        assert_eq!(actual_offset, offset);
1812                        assert_eq!(actual_composite.size(), composite_offset);
1813                        assert_eq!(actual_composite.align(), composite.align.get());
1814                    } else {
1815                        // An error here reflects that composite of `base`
1816                        // and `field` cannot correspond to a real Rust type
1817                        // fragment, because such a fragment would violate
1818                        // the basic invariants of a valid Rust layout. At
1819                        // the time of writing, `DstLayout` is a little more
1820                        // permissive than `Layout`, so we don't assert
1821                        // anything in this branch (e.g., unreachability).
1822                    }
1823                } else {
1824                    panic!("The extension of a layout with a DST must result in a DST.")
1825                }
1826            }
1827        }
1828    }
1829
1830    #[kani::proof]
1831    #[kani::should_panic]
1832    fn prove_dst_layout_extend_dst_panics() {
1833        let base: DstLayout = kani::any();
1834        let field: DstLayout = kani::any();
1835        let packed: Option<NonZeroUsize> = kani::any();
1836
1837        if let Some(max_align) = packed {
1838            kani::assume(max_align.is_power_of_two());
1839            kani::assume(base.align <= max_align);
1840        }
1841
1842        kani::assume(matches!(base.size_info, SizeInfo::SliceDst(..)));
1843
1844        let _ = base.extend(field, packed);
1845    }
1846
1847    #[kani::proof]
1848    fn prove_dst_layout_pad_to_align() {
1849        use crate::util::padding_needed_for;
1850
1851        let layout: DstLayout = kani::any();
1852
1853        let padded: DstLayout = layout.pad_to_align();
1854
1855        // Calling `pad_to_align` does not alter the `DstLayout`'s alignment.
1856        assert_eq!(padded.align, layout.align);
1857
1858        if let SizeInfo::Sized { size: unpadded_size } = layout.size_info {
1859            if let SizeInfo::Sized { size: padded_size } = padded.size_info {
1860                // If the layout is sized, it will remain sized after padding is
1861                // added. Its sum will be its unpadded size and the size of the
1862                // trailing padding needed to satisfy its alignment
1863                // requirements.
1864                let padding = padding_needed_for(unpadded_size, layout.align);
1865                assert_eq!(padded_size, unpadded_size + padding);
1866
1867                // Prove that calling `DstLayout::pad_to_align` behaves
1868                // identically to `Layout::pad_to_align`.
1869                let layout_analog =
1870                    Layout::from_size_align(unpadded_size, layout.align.get()).unwrap();
1871                let padded_analog = layout_analog.pad_to_align();
1872                assert_eq!(padded_analog.align(), layout.align.get());
1873                assert_eq!(padded_analog.size(), padded_size);
1874            } else {
1875                panic!("The padding of a sized layout must result in a sized layout.")
1876            }
1877        } else {
1878            // If the layout is a DST, padding cannot be statically added.
1879            assert_eq!(padded.size_info, layout.size_info);
1880        }
1881    }
1882}