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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! Unchecked methods for working with the data inside GC objects.

use crate::V128;
use core::mem;

/// A plain-old-data type that can be stored in a `ValType` or a `StorageType`.
pub trait PodValType<const SIZE: usize>: Copy {
    /// Read an instance of `Self` from the given native-endian bytes.
    fn read_le(le_bytes: &[u8; SIZE]) -> Self;

    /// Write `self` into the given memory location, as native-endian bytes.
    fn write_le(&self, into: &mut [u8; SIZE]);
}

macro_rules! impl_pod_val_type {
    ( $( $t:ty , )* ) => {
        $(
            impl PodValType<{mem::size_of::<$t>()}> for $t {
                fn read_le(le_bytes: &[u8; mem::size_of::<$t>()]) -> Self {
                    <$t>::from_le_bytes(*le_bytes)
                }
                fn write_le(&self, into: &mut [u8; mem::size_of::<$t>()]) {
                    *into = self.to_le_bytes();
                }
            }
        )*
    };
}

impl_pod_val_type! {
    u8,
    u16,
    u32,
    u64,
    i8,
    i16,
    i32,
    i64,
}

impl PodValType<{ mem::size_of::<V128>() }> for V128 {
    fn read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self {
        u128::from_le_bytes(*le_bytes).into()
    }
    fn write_le(&self, into: &mut [u8; mem::size_of::<V128>()]) {
        *into = self.as_u128().to_le_bytes();
    }
}

/// The backing storage for a GC-managed object.
///
/// Methods on this type do not, generally, check against things like type
/// mismatches or that the given offset to read from even falls on a field
/// boundary. Omitting these checks is memory safe, due to our untrusted,
/// indexed GC heaps. Providing incorrect offsets will result in general
/// incorrectness, such as wrong answers or even panics, however.
///
/// Finally, these methods *will* panic on out-of-bounds accesses, either out of
/// the GC heap's bounds or out of this object's bounds. The former is necessary
/// for preserving the memory safety of indexed GC heaps in the face of (for
/// example) collector bugs, but the latter is just a defensive technique to
/// catch bugs early and prevent action at a distance as much as possible.
pub struct VMGcObjectDataMut<'a> {
    data: &'a mut [u8],
}

macro_rules! impl_pod_methods {
    ( $( $t:ty, $read:ident, $write:ident; )* ) => {
        $(
            /// Read a `
            #[doc = stringify!($t)]
            /// ` field this object.
            ///
            /// Panics on out-of-bounds accesses.
            #[inline]
            pub fn $read(&self, offset: u32) -> $t {
                self.read_pod::<{ mem::size_of::<$t>() }, $t>(offset)
            }

            /// Write a `
            #[doc = stringify!($t)]
            /// ` into this object.
            ///
            /// Panics on out-of-bounds accesses.
            #[inline]
            pub fn $write(&mut self, offset: u32, val: $t) {
                self.write_pod::<{ mem::size_of::<$t>() }, $t>(offset, val);
            }
        )*
    };
}

impl<'a> VMGcObjectDataMut<'a> {
    /// Construct a `VMStructDataMut` from the given slice of bytes.
    #[inline]
    pub fn new(data: &'a mut [u8]) -> Self {
        Self { data }
    }

    /// Read a POD field out of this object.
    ///
    /// Panics on out-of-bounds accesses.
    ///
    /// Don't generally use this method, use `read_u8`, `read_i64`,
    /// etc... instead.
    #[inline]
    fn read_pod<const N: usize, T>(&self, offset: u32) -> T
    where
        T: PodValType<N>,
    {
        assert_eq!(N, mem::size_of::<T>());
        let offset = usize::try_from(offset).unwrap();
        let end = offset.checked_add(N).unwrap();
        let bytes = self.data.get(offset..end).expect("out of bounds field");
        T::read_le(bytes.try_into().unwrap())
    }

    /// Read a POD field out of this object.
    ///
    /// Panics on out-of-bounds accesses.
    ///
    /// Don't generally use this method, use `write_u8`, `write_i64`,
    /// etc... instead.
    #[inline]
    fn write_pod<const N: usize, T>(&mut self, offset: u32, val: T)
    where
        T: PodValType<N>,
    {
        assert_eq!(N, mem::size_of::<T>());
        let offset = usize::try_from(offset).unwrap();
        let end = offset.checked_add(N).unwrap();
        let into = match self.data.get_mut(offset..end) {
            Some(into) => into,
            None => panic!(
                "out of bounds field! field range = {offset:#x}..{end:#x}; object len = {:#x}",
                self.data.len(),
            ),
        };
        val.write_le(into.try_into().unwrap());
    }

    /// Get a slice of this object's data.
    ///
    /// Panics on out-of-bounds accesses.
    #[inline]
    pub fn slice(&self, offset: u32, len: u32) -> &[u8] {
        let start = usize::try_from(offset).unwrap();
        let len = usize::try_from(len).unwrap();
        let end = start.checked_add(len).unwrap();
        self.data.get(start..end).expect("out of bounds slice")
    }

    /// Get a mutable slice of this object's data.
    ///
    /// Panics on out-of-bounds accesses.
    #[inline]
    pub fn slice_mut(&mut self, offset: u32, len: u32) -> &mut [u8] {
        let start = usize::try_from(offset).unwrap();
        let len = usize::try_from(len).unwrap();
        let end = start.checked_add(len).unwrap();
        self.data.get_mut(start..end).expect("out of bounds slice")
    }

    /// Copy the given slice into this object's data at the given offset.
    ///
    /// Panics on out-of-bounds accesses.
    #[inline]
    pub fn copy_from_slice(&mut self, offset: u32, src: &[u8]) {
        let offset = usize::try_from(offset).unwrap();
        let end = offset.checked_add(src.len()).unwrap();
        let into = self.data.get_mut(offset..end).expect("out of bounds copy");
        into.copy_from_slice(src);
    }

    impl_pod_methods! {
        u8, read_u8, write_u8;
        u16, read_u16, write_u16;
        u32, read_u32, write_u32;
        u64, read_u64, write_u64;
        i8, read_i8, write_i8;
        i16, read_i16, write_i16;
        i32, read_i32, write_i32;
        i64, read_i64, write_i64;
        V128, read_v128, write_v128;
    }
}