1 //! Unchecked methods for working with the data inside GC objects.
2 
3 use crate::V128;
4 use core::mem;
5 
6 /// A plain-old-data type that can be stored in a `ValType` or a `StorageType`.
7 pub trait PodValType<const SIZE: usize>: Copy {
8     /// Read an instance of `Self` from the given native-endian bytes.
read_le(le_bytes: &[u8; SIZE]) -> Self9     fn read_le(le_bytes: &[u8; SIZE]) -> Self;
10 
11     /// Write `self` into the given memory location, as native-endian bytes.
write_le(&self, into: &mut [u8; SIZE])12     fn write_le(&self, into: &mut [u8; SIZE]);
13 }
14 
15 macro_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 
30 impl_pod_val_type! {
31     u8,
32     u16,
33     u32,
34     u64,
35     i8,
36     i16,
37     i32,
38     i64,
39 }
40 
41 impl PodValType<{ mem::size_of::<V128>() }> for V128 {
read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self42     fn read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self {
43         u128::from_le_bytes(*le_bytes).into()
44     }
write_le(&self, into: &mut [u8; mem::size_of::<V128>()])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.
63 #[repr(transparent)]
64 pub struct VMGcObjectData {
65     data: [u8],
66 }
67 
68 macro_rules! impl_pod_methods {
69     ( $( $T:ty, $read:ident, $write:ident; )* ) => {
70         $(
71             /// Read a `
72             #[doc = stringify!($T)]
73             /// ` field this object.
74             ///
75             /// Panics on out-of-bounds accesses.
76             #[inline]
77             pub fn $read(&self, offset: u32) -> $T
78             {
79                 self.read_pod::<{ mem::size_of::<$T>() }, $T>(offset)
80             }
81 
82             /// Write a `
83             #[doc = stringify!($T)]
84             /// ` into this object.
85             ///
86             /// Panics on out-of-bounds accesses.
87             #[inline]
88             pub fn $write(&mut self, offset: u32, val: $T)
89             {
90                 self.write_pod::<{ mem::size_of::<$T>() }, $T>(offset, val);
91             }
92         )*
93     };
94 }
95 
96 impl<'a> From<&'a [u8]> for &'a VMGcObjectData {
97     #[inline]
from(data: &'a [u8]) -> Self98     fn from(data: &'a [u8]) -> Self {
99         &VMGcObjectData::from_slice(data)
100     }
101 }
102 
103 impl<'a> From<&'a mut [u8]> for &'a mut VMGcObjectData {
104     #[inline]
from(data: &'a mut [u8]) -> Self105     fn from(data: &'a mut [u8]) -> Self {
106         VMGcObjectData::from_slice_mut(data)
107     }
108 }
109 
110 impl VMGcObjectData {
111     /// Construct a `VMGcObjectData` from the given slice of bytes.
112     #[inline]
from_slice(data: &[u8]) -> &Self113     pub fn from_slice(data: &[u8]) -> &Self {
114         unsafe { mem::transmute(data) }
115     }
116 
117     /// Construct a `VMGcObjectData` from the given slice of bytes.
118     #[inline]
from_slice_mut(data: &mut [u8]) -> &mut Self119     pub fn from_slice_mut(data: &mut [u8]) -> &mut Self {
120         unsafe { mem::transmute(data) }
121     }
122 
123     /// Read a POD field out of this object.
124     ///
125     /// Panics on out-of-bounds accesses.
126     ///
127     /// Don't generally use this method, use `read_u8`, `read_i64`,
128     /// etc... instead.
129     #[inline]
read_pod<const N: usize, T>(&self, offset: u32) -> T where T: PodValType<N>,130     fn read_pod<const N: usize, T>(&self, offset: u32) -> T
131     where
132         T: PodValType<N>,
133     {
134         assert_eq!(N, mem::size_of::<T>());
135         let offset = usize::try_from(offset).unwrap();
136         let end = offset.checked_add(N).unwrap();
137         let bytes = self.data.get(offset..end).expect("out of bounds field");
138         T::read_le(bytes.try_into().unwrap())
139     }
140 
141     /// Read a POD field out of this object.
142     ///
143     /// Panics on out-of-bounds accesses.
144     ///
145     /// Don't generally use this method, use `write_u8`, `write_i64`,
146     /// etc... instead.
147     #[inline]
write_pod<const N: usize, T>(&mut self, offset: u32, val: T) where T: PodValType<N>,148     fn write_pod<const N: usize, T>(&mut self, offset: u32, val: T)
149     where
150         T: PodValType<N>,
151     {
152         assert_eq!(N, mem::size_of::<T>());
153         let offset = usize::try_from(offset).unwrap();
154         let end = offset.checked_add(N).unwrap();
155         let into = match self.data.get_mut(offset..end) {
156             Some(into) => into,
157             None => panic!(
158                 "out of bounds field! field range = {offset:#x}..{end:#x}; object len = {:#x}",
159                 self.data.as_mut().len(),
160             ),
161         };
162         val.write_le(into.try_into().unwrap());
163     }
164 
165     /// Get a slice of this object's data.
166     ///
167     /// Note that GC data is always stored in little-endian order, and this
168     /// method does not do any conversions to/from host endianness for you.
169     ///
170     /// Panics on out-of-bounds accesses.
171     #[inline]
slice(&self, offset: u32, len: u32) -> &[u8]172     pub fn slice(&self, offset: u32, len: u32) -> &[u8] {
173         let start = usize::try_from(offset).unwrap();
174         let len = usize::try_from(len).unwrap();
175         let end = start.checked_add(len).unwrap();
176         self.data.get(start..end).expect("out of bounds slice")
177     }
178 
179     /// Get a mutable slice of this object's data.
180     ///
181     /// Note that GC data is always stored in little-endian order, and this
182     /// method does not do any conversions to/from host endianness for you.
183     ///
184     /// Panics on out-of-bounds accesses.
185     #[inline]
slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8]186     pub fn slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8] {
187         let start = usize::try_from(offset).unwrap();
188         let len = usize::try_from(len).unwrap();
189         let end = start.checked_add(len).unwrap();
190         self.data.get_mut(start..end).expect("out of bounds slice")
191     }
192 
193     /// Copy the given slice into this object's data at the given offset.
194     ///
195     /// Note that GC data is always stored in little-endian order, and this
196     /// method does not do any conversions to/from host endianness for you.
197     ///
198     /// Panics on out-of-bounds accesses.
199     #[inline]
copy_from_slice(&mut self, offset: u32, src: &[u8])200     pub fn copy_from_slice(&mut self, offset: u32, src: &[u8]) {
201         let offset = usize::try_from(offset).unwrap();
202         let end = offset.checked_add(src.len()).unwrap();
203         let into = self.data.get_mut(offset..end).expect("out of bounds copy");
204         into.copy_from_slice(src);
205     }
206 
207     impl_pod_methods! {
208         u8, read_u8, write_u8;
209         u16, read_u16, write_u16;
210         u32, read_u32, write_u32;
211         u64, read_u64, write_u64;
212         i8, read_i8, write_i8;
213         i16, read_i16, write_i16;
214         i32, read_i32, write_i32;
215         i64, read_i64, write_i64;
216         V128, read_v128, write_v128;
217     }
218 }
219