wasmer_runtime_core_fl/memory/
ptr.rs

1//! Types for a reusable pointer abstraction for accessing Wasm linear memory.
2//!
3//! This abstraction is safe: it ensures the memory is in bounds and that the pointer
4//! is aligned (avoiding undefined behavior).
5//!
6//! Therefore, you should use this abstraction whenever possible to avoid memory
7//! related bugs when implementing an ABI.
8
9use crate::{
10    memory::Memory,
11    types::{ValueType, WasmExternType},
12};
13use std::{cell::Cell, fmt, marker::PhantomData, mem};
14
15/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
16/// to get access to methods
17pub struct Array;
18/// The `Item` marker type. This is the default and does not usually need to be
19/// specified.
20pub struct Item;
21
22/// A zero-cost type that represents a pointer to something in Wasm linear
23/// memory.
24///
25/// This type can be used directly in the host function arguments:
26/// ```
27/// # use wasmer_runtime_core::vm::Ctx;
28/// # use wasmer_runtime_core::memory::ptr::WasmPtr;
29/// pub fn host_import(ctx: &mut Ctx, ptr: WasmPtr<u32>) {
30///     let memory = ctx.memory(0);
31///     let derefed_ptr = ptr.deref(memory).expect("pointer in bounds");
32///     let inner_val: u32 = derefed_ptr.get();
33///     println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
34///     // update the value being pointed to
35///     derefed_ptr.set(inner_val + 1);
36/// }
37/// ```
38#[repr(transparent)]
39pub struct WasmPtr<T: Copy, Ty = Item> {
40    offset: u32,
41    _phantom: PhantomData<(T, Ty)>,
42}
43
44/// Methods relevant to all types of `WasmPtr`.
45impl<T: Copy, Ty> WasmPtr<T, Ty> {
46    /// Create a new `WasmPtr` at the given offset.
47    #[inline]
48    pub fn new(offset: u32) -> Self {
49        Self {
50            offset,
51            _phantom: PhantomData,
52        }
53    }
54
55    /// Get the offset into Wasm linear memory for this `WasmPtr`.
56    #[inline]
57    pub fn offset(self) -> u32 {
58        self.offset
59    }
60}
61
62#[inline(always)]
63fn align_pointer(ptr: usize, align: usize) -> usize {
64    // clears bits below aligment amount (assumes power of 2) to align pointer
65    debug_assert!(align.count_ones() == 1);
66    ptr & !(align - 1)
67}
68
69/// Methods for `WasmPtr`s to data that can be dereferenced, namely to types
70/// that implement [`ValueType`], meaning that they're valid for all possible
71/// bit patterns.
72impl<T: Copy + ValueType> WasmPtr<T, Item> {
73    /// Dereference the `WasmPtr` getting access to a `&Cell<T>` allowing for
74    /// reading and mutating of the inner value.
75    ///
76    /// This method is unsound if used with unsynchronized shared memory.
77    /// If you're unsure what that means, it likely does not apply to you.
78    /// This invariant will be enforced in the future.
79    #[inline]
80    pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
81        if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
82            || mem::size_of::<T>() == 0
83        {
84            return None;
85        }
86        unsafe {
87            let cell_ptr = align_pointer(
88                memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
89                mem::align_of::<T>(),
90            ) as *const Cell<T>;
91            Some(&*cell_ptr)
92        }
93    }
94
95    /// Mutably dereference this `WasmPtr` getting a `&mut Cell<T>` allowing for
96    /// direct access to a `&mut T`.
97    ///
98    /// # Safety
99    /// - This method does not do any aliasing checks: it's possible to create
100    ///  `&mut T` that point to the same memory. You should ensure that you have
101    ///   exclusive access to Wasm linear memory before calling this method.
102    #[inline]
103    pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
104        if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
105            || mem::size_of::<T>() == 0
106        {
107            return None;
108        }
109        let cell_ptr = align_pointer(
110            memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
111            mem::align_of::<T>(),
112        ) as *mut Cell<T>;
113        Some(&mut *cell_ptr)
114    }
115}
116
117/// Methods for `WasmPtr`s to arrays of data that can be dereferenced, namely to
118/// types that implement [`ValueType`], meaning that they're valid for all
119/// possible bit patterns.
120impl<T: Copy + ValueType> WasmPtr<T, Array> {
121    /// Dereference the `WasmPtr` getting access to a `&[Cell<T>]` allowing for
122    /// reading and mutating of the inner values.
123    ///
124    /// This method is unsound if used with unsynchronized shared memory.
125    /// If you're unsure what that means, it likely does not apply to you.
126    /// This invariant will be enforced in the future.
127    #[inline]
128    pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
129        // gets the size of the item in the array with padding added such that
130        // for any index, we will always result an aligned memory access
131        let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
132        let slice_full_len = index as usize + length as usize;
133        let memory_size = memory.size().bytes().0;
134
135        if (self.offset as usize) + (item_size * slice_full_len) > memory_size
136            || self.offset as usize >= memory_size
137            || mem::size_of::<T>() == 0
138        {
139            return None;
140        }
141
142        unsafe {
143            let cell_ptr = align_pointer(
144                memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
145                mem::align_of::<T>(),
146            ) as *const Cell<T>;
147            let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
148                [index as usize..slice_full_len];
149            Some(cell_ptrs)
150        }
151    }
152
153    /// Mutably dereference this `WasmPtr` getting a `&mut [Cell<T>]` allowing for
154    /// direct access to a `&mut [T]`.
155    ///
156    /// # Safety
157    /// - This method does not do any aliasing checks: it's possible to create
158    ///  `&mut T` that point to the same memory. You should ensure that you have
159    ///   exclusive access to Wasm linear memory before calling this method.
160    #[inline]
161    pub unsafe fn deref_mut(
162        self,
163        memory: &Memory,
164        index: u32,
165        length: u32,
166    ) -> Option<&mut [Cell<T>]> {
167        // gets the size of the item in the array with padding added such that
168        // for any index, we will always result an aligned memory access
169        let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
170        let slice_full_len = index as usize + length as usize;
171        let memory_size = memory.size().bytes().0;
172
173        if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
174            || self.offset as usize >= memory_size
175            || mem::size_of::<T>() == 0
176        {
177            return None;
178        }
179
180        let cell_ptr = align_pointer(
181            memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
182            mem::align_of::<T>(),
183        ) as *mut Cell<T>;
184        let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
185            [index as usize..slice_full_len];
186        Some(cell_ptrs)
187    }
188
189    /// Get a UTF-8 string from the `WasmPtr` with the given length.
190    ///
191    /// Note that this method returns a reference to Wasm linear memory. The
192    /// underlying data can be mutated if the Wasm is allowed to execute or
193    /// an aliasing `WasmPtr` is used to mutate memory.
194    pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
195        let memory_size = memory.size().bytes().0;
196
197        if self.offset as usize + str_len as usize > memory.size().bytes().0
198            || self.offset as usize >= memory_size
199        {
200            return None;
201        }
202        let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
203        let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, str_len as usize) };
204        std::str::from_utf8(slice).ok()
205    }
206
207    /// Get a UTF-8 string from the `WasmPtr`, where the string is nul-terminated.
208    ///
209    /// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
210    /// [`get_utf8_string`] has to be used for those.
211    ///
212    /// Also note that this method returns a reference to Wasm linear memory. The
213    /// underlying data can be mutated if the Wasm is allowed to execute or
214    /// an aliasing `WasmPtr` is used to mutate memory.
215    pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> {
216        memory.view::<u8>()[(self.offset as usize)..]
217            .iter()
218            .map(|cell| cell.get())
219            .position(|byte| byte == 0)
220            .and_then(|length| self.get_utf8_string(memory, length as u32))
221    }
222}
223
224unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
225    type Native = i32;
226
227    fn to_native(self) -> Self::Native {
228        self.offset as i32
229    }
230    fn from_native(n: Self::Native) -> Self {
231        Self {
232            offset: n as u32,
233            _phantom: PhantomData,
234        }
235    }
236}
237
238unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
239
240impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
241    fn clone(&self) -> Self {
242        Self {
243            offset: self.offset,
244            _phantom: PhantomData,
245        }
246    }
247}
248
249impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
250
251impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
252    fn eq(&self, other: &Self) -> bool {
253        self.offset == other.offset
254    }
255}
256
257impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
258
259impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
260    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261        write!(f, "WasmPtr({:#x})", self.offset)
262    }
263}
264
265#[cfg(test)]
266mod test {
267    use super::*;
268    use crate::memory;
269    use crate::units::Pages;
270
271    /// Ensure that memory accesses work on the edges of memory and that out of
272    /// bounds errors are caught with both `deref` and `deref_mut`.
273    #[test]
274    fn wasm_ptr_memory_bounds_checks_hold() {
275        // create a memory
276        let memory_descriptor =
277            memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
278        let memory = memory::Memory::new(memory_descriptor).unwrap();
279
280        // test that basic access works and that len = 0 works, but oob does not
281        let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
282        let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
283
284        assert!(start_wasm_ptr.deref(&memory).is_some());
285        assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
286        assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
287        assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
288        assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
289        assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
290        assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
291
292        // test that accessing the last valid memory address works correctly and OOB is caught
293        let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
294        let end_wasm_ptr: WasmPtr<u8> = WasmPtr::new(last_valid_address_for_u8);
295        assert!(end_wasm_ptr.deref(&memory).is_some());
296        assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
297
298        let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
299
300        assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
301        assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
302        let invalid_idx_len_combos: [(u32, u32); 3] =
303            [(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
304        for &(idx, len) in invalid_idx_len_combos.iter() {
305            assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
306            assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
307        }
308        assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
309
310        // test that accesing the last valid memory address for a u32 is valid
311        // (same as above test but with more edge cases to assert on)
312        let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32;
313        let end_wasm_ptr: WasmPtr<u32> = WasmPtr::new(last_valid_address_for_u32);
314        assert!(end_wasm_ptr.deref(&memory).is_some());
315        assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
316        assert!(end_wasm_ptr.deref(&memory).is_some());
317        assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
318
319        let end_wasm_ptr_oob_array: [WasmPtr<u32>; 4] = [
320            WasmPtr::new(last_valid_address_for_u32 + 1),
321            WasmPtr::new(last_valid_address_for_u32 + 2),
322            WasmPtr::new(last_valid_address_for_u32 + 3),
323            WasmPtr::new(last_valid_address_for_u32 + 4),
324        ];
325        for oob_end_ptr in end_wasm_ptr_oob_array.iter() {
326            assert!(oob_end_ptr.deref(&memory).is_none());
327            assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
328        }
329        let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
330        assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
331        assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
332
333        let invalid_idx_len_combos: [(u32, u32); 3] =
334            [(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
335        for &(idx, len) in invalid_idx_len_combos.iter() {
336            assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
337            assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
338        }
339
340        let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
341            WasmPtr::new(last_valid_address_for_u32 + 1),
342            WasmPtr::new(last_valid_address_for_u32 + 2),
343            WasmPtr::new(last_valid_address_for_u32 + 3),
344            WasmPtr::new(last_valid_address_for_u32 + 4),
345        ];
346
347        for oob_end_array_ptr in end_wasm_ptr_array_oob_array.iter() {
348            assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
349            assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
350            assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
351            assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
352        }
353    }
354}