wasmtime/runtime/vm/gc/enabled/
data.rs

1//! Unchecked methods for working with the data inside GC objects.
2
3use crate::V128;
4use core::mem;
5
6/// A plain-old-data type that can be stored in a `ValType` or a `StorageType`.
7pub trait PodValType<const SIZE: usize>: Copy {
8    /// Read an instance of `Self` from the given native-endian bytes.
9    fn read_le(le_bytes: &[u8; SIZE]) -> Self;
10
11    /// Write `self` into the given memory location, as native-endian bytes.
12    fn write_le(&self, into: &mut [u8; SIZE]);
13}
14
15macro_rules! impl_pod_val_type {
16    ( $( $t:ty , )* ) => {
17        $(
18            impl PodValType<{mem::size_of::<$t>()}> for $t {
19                fn read_le(le_bytes: &[u8; mem::size_of::<$t>()]) -> Self {
20                    <$t>::from_le_bytes(*le_bytes)
21                }
22                fn write_le(&self, into: &mut [u8; mem::size_of::<$t>()]) {
23                    *into = self.to_le_bytes();
24                }
25            }
26        )*
27    };
28}
29
30impl_pod_val_type! {
31    u8,
32    u16,
33    u32,
34    u64,
35    i8,
36    i16,
37    i32,
38    i64,
39}
40
41impl PodValType<{ mem::size_of::<V128>() }> for V128 {
42    fn read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self {
43        u128::from_le_bytes(*le_bytes).into()
44    }
45    fn write_le(&self, into: &mut [u8; mem::size_of::<V128>()]) {
46        *into = self.as_u128().to_le_bytes();
47    }
48}
49
50/// The backing storage for a GC-managed object.
51///
52/// Methods on this type do not, generally, check against things like type
53/// mismatches or that the given offset to read from even falls on a field
54/// boundary. Omitting these checks is memory safe, due to our untrusted,
55/// indexed GC heaps. Providing incorrect offsets will result in general
56/// incorrectness, such as wrong answers or even panics, however.
57///
58/// Finally, these methods *will* panic on out-of-bounds accesses, either out of
59/// the GC heap's bounds or out of this object's bounds. The former is necessary
60/// for preserving the memory safety of indexed GC heaps in the face of (for
61/// example) collector bugs, but the latter is just a defensive technique to
62/// catch bugs early and prevent action at a distance as much as possible.
63pub struct VMGcObjectDataMut<'a> {
64    data: &'a mut [u8],
65}
66
67macro_rules! impl_pod_methods {
68    ( $( $t:ty, $read:ident, $write:ident; )* ) => {
69        $(
70            /// Read a `
71            #[doc = stringify!($t)]
72            /// ` field this object.
73            ///
74            /// Panics on out-of-bounds accesses.
75            #[inline]
76            pub fn $read(&self, offset: u32) -> $t {
77                self.read_pod::<{ mem::size_of::<$t>() }, $t>(offset)
78            }
79
80            /// Write a `
81            #[doc = stringify!($t)]
82            /// ` into this object.
83            ///
84            /// Panics on out-of-bounds accesses.
85            #[inline]
86            pub fn $write(&mut self, offset: u32, val: $t) {
87                self.write_pod::<{ mem::size_of::<$t>() }, $t>(offset, val);
88            }
89        )*
90    };
91}
92
93impl<'a> VMGcObjectDataMut<'a> {
94    /// Construct a `VMStructDataMut` from the given slice of bytes.
95    #[inline]
96    pub fn new(data: &'a mut [u8]) -> Self {
97        Self { data }
98    }
99
100    /// Read a POD field out of this object.
101    ///
102    /// Panics on out-of-bounds accesses.
103    ///
104    /// Don't generally use this method, use `read_u8`, `read_i64`,
105    /// etc... instead.
106    #[inline]
107    fn read_pod<const N: usize, T>(&self, offset: u32) -> T
108    where
109        T: PodValType<N>,
110    {
111        assert_eq!(N, mem::size_of::<T>());
112        let offset = usize::try_from(offset).unwrap();
113        let end = offset.checked_add(N).unwrap();
114        let bytes = self.data.get(offset..end).expect("out of bounds field");
115        T::read_le(bytes.try_into().unwrap())
116    }
117
118    /// Read a POD field out of this object.
119    ///
120    /// Panics on out-of-bounds accesses.
121    ///
122    /// Don't generally use this method, use `write_u8`, `write_i64`,
123    /// etc... instead.
124    #[inline]
125    fn write_pod<const N: usize, T>(&mut self, offset: u32, val: T)
126    where
127        T: PodValType<N>,
128    {
129        assert_eq!(N, mem::size_of::<T>());
130        let offset = usize::try_from(offset).unwrap();
131        let end = offset.checked_add(N).unwrap();
132        let into = match self.data.get_mut(offset..end) {
133            Some(into) => into,
134            None => panic!(
135                "out of bounds field! field range = {offset:#x}..{end:#x}; object len = {:#x}",
136                self.data.len(),
137            ),
138        };
139        val.write_le(into.try_into().unwrap());
140    }
141
142    /// Get a slice of this object's data.
143    ///
144    /// Panics on out-of-bounds accesses.
145    #[inline]
146    pub fn slice(&self, offset: u32, len: u32) -> &[u8] {
147        let start = usize::try_from(offset).unwrap();
148        let len = usize::try_from(len).unwrap();
149        let end = start.checked_add(len).unwrap();
150        self.data.get(start..end).expect("out of bounds slice")
151    }
152
153    /// Get a mutable slice of this object's data.
154    ///
155    /// Panics on out-of-bounds accesses.
156    #[inline]
157    pub fn slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8] {
158        let start = usize::try_from(offset).unwrap();
159        let len = usize::try_from(len).unwrap();
160        let end = start.checked_add(len).unwrap();
161        self.data.get_mut(start..end).expect("out of bounds slice")
162    }
163
164    /// Copy the given slice into this object's data at the given offset.
165    ///
166    /// Panics on out-of-bounds accesses.
167    #[inline]
168    pub fn copy_from_slice(&mut self, offset: u32, src: &[u8]) {
169        let offset = usize::try_from(offset).unwrap();
170        let end = offset.checked_add(src.len()).unwrap();
171        let into = self.data.get_mut(offset..end).expect("out of bounds copy");
172        into.copy_from_slice(src);
173    }
174
175    impl_pod_methods! {
176        u8, read_u8, write_u8;
177        u16, read_u16, write_u16;
178        u32, read_u32, write_u32;
179        u64, read_u64, write_u64;
180        i8, read_i8, write_i8;
181        i16, read_i16, write_i16;
182        i32, read_i32, write_i32;
183        i64, read_i64, write_i64;
184        V128, read_v128, write_v128;
185    }
186}