Skip to main content

multiboot2_common/
tag.rs

1//! Module for the traits [`MaybeDynSized`] and [`Tag`].
2
3use crate::{DynSizedStructure, Header};
4use core::slice;
5use ptr_meta::Pointee;
6
7/// A trait to abstract sized and unsized structures (DSTs). It enables
8/// casting a [`DynSizedStructure`] to sized or unsized structures using
9/// [`DynSizedStructure::cast`].
10///
11/// Structs that are a DST must provide a **correct** [`MaybeDynSized::dst_len`]
12/// implementation. The needed metadata type is either `()` for sized types or
13/// `usize` for dynamically sized types. For sized types, there is a default
14/// implementation. Only dynamically sized types need to implement
15/// [`MaybeDynSized::dst_len`].
16///
17/// # Safety
18///
19/// Implementors must be `#[repr(C)]`, start with `Self::Header`, have an
20/// alignment of at most [`ALIGNMENT`], and allow every bit pattern.
21///
22/// [`MaybeDynSized::BASE_SIZE`], [`MaybeDynSized::dst_len`], and
23/// [`Header::total_size`] must correctly describe the initialized,
24/// contiguous memory backing the value. Incorrect sizes or implicit padding
25/// within the reported range can cause out-of-bounds references. Trailing
26/// padding beyond that range is fine.
27///
28/// Note that for sized implementors, the requirements above imply that
29/// `size_of::<Self>()` exceeds [`MaybeDynSized::BASE_SIZE`] at most by
30/// trailing padding up to the type's alignment. Same-size casts rely on this
31/// to keep the created reference within the source allocation.
32///
33/// [`ID`]: Tag::ID
34/// [`ALIGNMENT`]: crate::ALIGNMENT
35/// [`DynSizedStructure`]: crate::DynSizedStructure
36pub unsafe trait MaybeDynSized: Pointee {
37    /// The associated [`Header`] of this tag.
38    type Header: Header;
39
40    /// The true base size of the struct without any implicit or additional
41    /// padding. Note that `size_of::<T>()` isn't sufficient, as for example
42    /// the type could have three `u32` fields, which would add an implicit
43    /// `u32` padding. However, this constant **must always** fulfill
44    /// `BASE_SIZE >= size_of::<Self::Header>()`.
45    ///
46    /// The main purpose of this constant is to create awareness when you
47    /// implement [`Self::dst_len`], where you should use this. If this value
48    /// is correct, we prevent situations where we read uninitialized bytes,
49    /// especially when creating tags in builders.
50    const BASE_SIZE: usize;
51
52    /// Returns the amount of items in the dynamically sized portion of the
53    /// DST. Note that this is not the amount of bytes. So if the dynamically
54    /// sized portion is 16 bytes in size and each element is 4 bytes big, then
55    /// this function must return 4.
56    ///
57    /// For sized tags, this just returns `()`. For DSTs, this returns an
58    /// `usize`.
59    fn dst_len(header: &Self::Header) -> Self::Metadata
60    where
61        // Either `()` or `usize`, never something else
62        Self::Metadata: Default,
63    {
64        let _ = header;
65        Default::default()
66    }
67
68    /// Returns the corresponding [`Header`].
69    fn header(&self) -> &Self::Header {
70        let ptr = &raw const *self;
71        // SAFETY: `self` is a valid reference and `Self::Header` is the
72        // prefix of this `repr(C)` structure at the same address.
73        unsafe { &*ptr.cast::<Self::Header>() }
74    }
75
76    /// Returns the payload, i.e., all memory that is not occupied by the
77    /// [`Header`] of the type. Implicit trailing padding beyond the
78    /// structure size reported in the header is not part of the payload.
79    ///
80    /// # Panics
81    /// Panics if the size reported in the header is smaller than the size of
82    /// the [`Header`] itself, which can only happen for oddly formed values.
83    fn payload(&self) -> &[u8] {
84        let from = size_of::<Self::Header>();
85        &self.as_bytes()[from..]
86    }
87
88    /// Returns the bytes of the structure, i.e., the header and the payload,
89    /// up to the structure size reported by [`Self::header`].
90    ///
91    /// Implicit trailing padding that the Rust memory layout might add beyond
92    /// that size is excluded, as it may be uninitialized for stack-constructed
93    /// values and must never be read.
94    fn as_bytes(&self) -> &[u8] {
95        let ptr = &raw const *self;
96        // Clamp to the allocation: a corrupt header size must never cause an
97        // out-of-bounds slice.
98        let size = self.header().total_size().min(size_of_val(self));
99        // SAFETY: `ptr` points to `self`'s allocation, `size` is in bounds,
100        // and the first `total_size()` bytes of a value are initialized.
101        unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) }
102    }
103
104    /// Returns a pointer to this structure.
105    fn as_ptr(&self) -> *const Self::Header {
106        self.as_bytes().as_ptr().cast()
107    }
108}
109
110/// Extension of [`MaybeDynSized`] for Tags.
111pub trait Tag: MaybeDynSized {
112    /// The ID type that identifies the tag.
113    type IDType: PartialEq + Eq;
114
115    /// The ID of this tag. This should be unique across all implementors.
116    ///
117    /// Although the ID is not yet used in `multiboot2-common`, it ensures
118    /// a consistent API in consumer crates.
119    const ID: Self::IDType;
120}
121
122// This implementation is not needed for parsing but for creation, when
123// downstream types just wrap this type.
124// SAFETY: `DynSizedStructure` is repr(C) with the header as first field,
125// any bit pattern is valid, and `BASE_SIZE`/`dst_len` match the ABI.
126unsafe impl<H: Header> MaybeDynSized for DynSizedStructure<H> {
127    type Header = H;
128
129    const BASE_SIZE: usize = size_of::<H>();
130
131    fn dst_len(header: &Self::Header) -> Self::Metadata {
132        header.payload_len()
133    }
134}