2020-03-03 16:16:29 -08:00
|
|
|
//! Types for a reusable pointer abstraction for accessing Wasm linear memory.
|
2019-06-11 11:10:42 +02:00
|
|
|
//!
|
|
|
|
//! This abstraction is safe: it ensures the memory is in bounds and that the pointer
|
|
|
|
//! is aligned (avoiding undefined behavior).
|
|
|
|
//!
|
|
|
|
//! Therefore, you should use this abstraction whenever possible to avoid memory
|
|
|
|
//! related bugs when implementing an ABI.
|
|
|
|
|
|
|
|
use crate::{
|
2019-06-06 15:45:19 -07:00
|
|
|
memory::Memory,
|
|
|
|
types::{ValueType, WasmExternType},
|
|
|
|
};
|
2019-06-11 11:10:42 +02:00
|
|
|
use std::{cell::Cell, fmt, marker::PhantomData, mem};
|
2019-06-06 15:45:19 -07:00
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// The `Array` marker type. This type can be used like `WasmPtr<T, Array>`
|
|
|
|
/// to get access to methods
|
2019-06-06 15:45:19 -07:00
|
|
|
pub struct Array;
|
2020-03-03 16:16:29 -08:00
|
|
|
/// The `Item` marker type. This is the default and does not usually need to be
|
|
|
|
/// specified.
|
2019-06-06 15:45:19 -07:00
|
|
|
pub struct Item;
|
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// A zero-cost type that represents a pointer to something in Wasm linear
|
|
|
|
/// memory.
|
|
|
|
///
|
|
|
|
/// This type can be used directly in the host function arguments:
|
|
|
|
/// ```
|
|
|
|
/// # use wasmer_runtime_core::vm::Ctx;
|
|
|
|
/// # use wasmer_runtime_core::memory::ptr::WasmPtr;
|
|
|
|
/// pub fn host_import(ctx: &mut Ctx, ptr: WasmPtr<u32>) {
|
|
|
|
/// let memory = ctx.memory(0);
|
|
|
|
/// let derefed_ptr = ptr.deref(memory).expect("pointer in bounds");
|
|
|
|
/// let inner_val: u32 = derefed_ptr.get();
|
|
|
|
/// println!("Got {} from Wasm memory address 0x{:X}", inner_val, ptr.offset());
|
|
|
|
/// // update the value being pointed to
|
|
|
|
/// derefed_ptr.set(inner_val + 1);
|
|
|
|
/// }
|
|
|
|
/// ```
|
2019-06-06 15:45:19 -07:00
|
|
|
#[repr(transparent)]
|
|
|
|
pub struct WasmPtr<T: Copy, Ty = Item> {
|
|
|
|
offset: u32,
|
|
|
|
_phantom: PhantomData<(T, Ty)>,
|
|
|
|
}
|
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Methods relevant to all types of `WasmPtr`.
|
2019-06-06 15:45:19 -07:00
|
|
|
impl<T: Copy, Ty> WasmPtr<T, Ty> {
|
2019-11-10 13:13:18 -06:00
|
|
|
/// Create a new `WasmPtr` at the given offset.
|
2019-06-06 15:45:19 -07:00
|
|
|
#[inline]
|
|
|
|
pub fn new(offset: u32) -> Self {
|
|
|
|
Self {
|
|
|
|
offset,
|
|
|
|
_phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Get the offset into Wasm linear memory for this `WasmPtr`.
|
2019-06-06 15:45:19 -07:00
|
|
|
#[inline]
|
2019-06-11 11:10:42 +02:00
|
|
|
pub fn offset(self) -> u32 {
|
|
|
|
self.offset
|
2019-06-06 15:45:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn align_pointer(ptr: usize, align: usize) -> usize {
|
|
|
|
// clears bits below aligment amount (assumes power of 2) to align pointer
|
|
|
|
debug_assert!(align.count_ones() == 1);
|
|
|
|
ptr & !(align - 1)
|
|
|
|
}
|
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Methods for `WasmPtr`s to data that can be dereferenced, namely to types
|
|
|
|
/// that implement [`ValueType`], meaning that they're valid for all possible
|
|
|
|
/// bit patterns.
|
2019-06-06 15:45:19 -07:00
|
|
|
impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Dereference the `WasmPtr` getting access to a `&Cell<T>` allowing for
|
|
|
|
/// reading and mutating of the inner value.
|
|
|
|
///
|
2020-03-03 17:04:52 -08:00
|
|
|
/// This method is unsound if used with unsynchronized shared memory.
|
2020-03-03 16:16:29 -08:00
|
|
|
/// If you're unsure what that means, it likely does not apply to you.
|
|
|
|
/// This invariant will be enforced in the future.
|
2019-06-06 15:45:19 -07:00
|
|
|
#[inline]
|
|
|
|
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
|
2020-03-05 13:23:50 -08:00
|
|
|
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|
|
|
|
|| mem::size_of::<T>() == 0
|
|
|
|
{
|
2019-06-06 15:45:19 -07:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
unsafe {
|
|
|
|
let cell_ptr = align_pointer(
|
|
|
|
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
|
|
|
|
mem::align_of::<T>(),
|
|
|
|
) as *const Cell<T>;
|
|
|
|
Some(&*cell_ptr)
|
|
|
|
}
|
|
|
|
}
|
2019-06-28 17:14:00 -07:00
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Mutably dereference this `WasmPtr` getting a `&mut Cell<T>` allowing for
|
|
|
|
/// direct access to a `&mut T`.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
/// - This method does not do any aliasing checks: it's possible to create
|
|
|
|
/// `&mut T` that point to the same memory. You should ensure that you have
|
|
|
|
/// exclusive access to Wasm linear memory before calling this method.
|
2019-06-28 17:14:00 -07:00
|
|
|
#[inline]
|
|
|
|
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
|
2020-03-05 13:23:50 -08:00
|
|
|
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|
|
|
|
|| mem::size_of::<T>() == 0
|
|
|
|
{
|
2019-06-28 17:14:00 -07:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let cell_ptr = align_pointer(
|
|
|
|
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
|
|
|
|
mem::align_of::<T>(),
|
|
|
|
) as *mut Cell<T>;
|
|
|
|
Some(&mut *cell_ptr)
|
|
|
|
}
|
2019-06-06 15:45:19 -07:00
|
|
|
}
|
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Methods for `WasmPtr`s to arrays of data that can be dereferenced, namely to
|
|
|
|
/// types that implement [`ValueType`], meaning that they're valid for all
|
|
|
|
/// possible bit patterns.
|
2019-06-06 15:45:19 -07:00
|
|
|
impl<T: Copy + ValueType> WasmPtr<T, Array> {
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Dereference the `WasmPtr` getting access to a `&[Cell<T>]` allowing for
|
|
|
|
/// reading and mutating of the inner values.
|
|
|
|
///
|
2020-03-03 17:04:52 -08:00
|
|
|
/// This method is unsound if used with unsynchronized shared memory.
|
2020-03-03 16:16:29 -08:00
|
|
|
/// If you're unsure what that means, it likely does not apply to you.
|
|
|
|
/// This invariant will be enforced in the future.
|
2019-06-06 15:45:19 -07:00
|
|
|
#[inline]
|
2020-01-24 14:55:02 -08:00
|
|
|
pub fn deref(self, memory: &Memory, index: u32, length: u32) -> Option<&[Cell<T>]> {
|
2019-06-06 15:45:19 -07:00
|
|
|
// gets the size of the item in the array with padding added such that
|
|
|
|
// for any index, we will always result an aligned memory access
|
|
|
|
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
2019-06-06 16:19:15 -07:00
|
|
|
let slice_full_len = index as usize + length as usize;
|
|
|
|
|
2020-03-05 13:23:50 -08:00
|
|
|
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|
|
|
|
|| length == 0
|
|
|
|
|| mem::size_of::<T>() == 0
|
|
|
|
{
|
2019-06-06 16:19:15 -07:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2019-06-06 15:45:19 -07:00
|
|
|
unsafe {
|
2019-06-06 16:19:15 -07:00
|
|
|
let cell_ptr = align_pointer(
|
|
|
|
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
|
|
|
|
mem::align_of::<T>(),
|
|
|
|
) as *const Cell<T>;
|
|
|
|
let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
|
2019-06-11 11:10:42 +02:00
|
|
|
[index as usize..slice_full_len];
|
2019-06-06 16:19:15 -07:00
|
|
|
Some(cell_ptrs)
|
2019-06-06 15:45:19 -07:00
|
|
|
}
|
|
|
|
}
|
2019-06-28 17:14:00 -07:00
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Mutably dereference this `WasmPtr` getting a `&mut [Cell<T>]` allowing for
|
|
|
|
/// direct access to a `&mut [T]`.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
/// - This method does not do any aliasing checks: it's possible to create
|
|
|
|
/// `&mut T` that point to the same memory. You should ensure that you have
|
|
|
|
/// exclusive access to Wasm linear memory before calling this method.
|
2019-06-28 17:14:00 -07:00
|
|
|
#[inline]
|
2020-01-24 14:55:02 -08:00
|
|
|
pub unsafe fn deref_mut(
|
2019-06-28 17:14:00 -07:00
|
|
|
self,
|
2020-01-24 14:55:02 -08:00
|
|
|
memory: &Memory,
|
2019-06-28 17:14:00 -07:00
|
|
|
index: u32,
|
|
|
|
length: u32,
|
2020-01-24 14:55:02 -08:00
|
|
|
) -> Option<&mut [Cell<T>]> {
|
2019-06-28 17:14:00 -07:00
|
|
|
// gets the size of the item in the array with padding added such that
|
|
|
|
// for any index, we will always result an aligned memory access
|
|
|
|
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
|
|
|
|
let slice_full_len = index as usize + length as usize;
|
|
|
|
|
2020-03-05 13:23:50 -08:00
|
|
|
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|
|
|
|
|| length == 0
|
|
|
|
|| mem::size_of::<T>() == 0
|
|
|
|
{
|
2019-06-28 17:14:00 -07:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cell_ptr = align_pointer(
|
|
|
|
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
|
|
|
|
mem::align_of::<T>(),
|
|
|
|
) as *mut Cell<T>;
|
|
|
|
let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
|
|
|
|
[index as usize..slice_full_len];
|
|
|
|
Some(cell_ptrs)
|
|
|
|
}
|
2019-07-12 15:58:28 -07:00
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Get a UTF-8 string from the `WasmPtr` with the given length.
|
|
|
|
///
|
|
|
|
/// Note that this method returns a reference to Wasm linear memory. The
|
|
|
|
/// underlying data can be mutated if the Wasm is allowed to execute or
|
|
|
|
/// an aliasing `WasmPtr` is used to mutate memory.
|
2020-01-24 14:55:02 -08:00
|
|
|
pub fn get_utf8_string(self, memory: &Memory, str_len: u32) -> Option<&str> {
|
2020-03-05 13:31:37 -08:00
|
|
|
if self.offset as usize + str_len as usize > memory.size().bytes().0 || str_len == 0 {
|
2019-07-12 15:58:28 -07:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let ptr = unsafe { memory.view::<u8>().as_ptr().add(self.offset as usize) as *const u8 };
|
|
|
|
let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, str_len as usize) };
|
|
|
|
std::str::from_utf8(slice).ok()
|
|
|
|
}
|
2019-12-20 16:29:44 +01:00
|
|
|
|
2020-03-03 16:16:29 -08:00
|
|
|
/// Get a UTF-8 string from the `WasmPtr`, where the string is nul-terminated.
|
|
|
|
///
|
2019-12-20 16:29:44 +01:00
|
|
|
/// Note that this does not account for UTF-8 strings that _contain_ nul themselves,
|
|
|
|
/// [`get_utf8_string`] has to be used for those.
|
2020-03-03 16:16:29 -08:00
|
|
|
///
|
|
|
|
/// Also note that this method returns a reference to Wasm linear memory. The
|
|
|
|
/// underlying data can be mutated if the Wasm is allowed to execute or
|
|
|
|
/// an aliasing `WasmPtr` is used to mutate memory.
|
2020-01-24 14:55:02 -08:00
|
|
|
pub fn get_utf8_string_with_nul(self, memory: &Memory) -> Option<&str> {
|
2019-12-20 16:29:44 +01:00
|
|
|
memory.view::<u8>()[(self.offset as usize)..]
|
|
|
|
.iter()
|
|
|
|
.map(|cell| cell.get())
|
|
|
|
.position(|byte| byte == 0)
|
|
|
|
.and_then(|length| self.get_utf8_string(memory, length as u32))
|
|
|
|
}
|
2019-06-06 15:45:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
|
|
|
|
type Native = i32;
|
|
|
|
|
|
|
|
fn to_native(self) -> Self::Native {
|
|
|
|
self.offset as i32
|
|
|
|
}
|
|
|
|
fn from_native(n: Self::Native) -> Self {
|
|
|
|
Self {
|
|
|
|
offset: n as u32,
|
|
|
|
_phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
|
|
|
|
|
|
|
|
impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
offset: self.offset,
|
|
|
|
_phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
|
|
|
|
|
|
|
|
impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.offset == other.offset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
|
|
|
|
|
|
|
|
impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "WasmPtr({:#x})", self.offset)
|
|
|
|
}
|
|
|
|
}
|
2020-03-05 15:06:00 -08:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
use crate::memory;
|
|
|
|
use crate::units::Pages;
|
|
|
|
|
|
|
|
/// Ensure that memory accesses work on the edges of memory and that out of
|
|
|
|
/// bounds errors are caught with both `deref` and `deref_mut`.
|
|
|
|
#[test]
|
|
|
|
fn wasm_ptr_memory_bounds_checks_hold() {
|
|
|
|
// create a memory
|
|
|
|
let memory_descriptor =
|
|
|
|
memory::MemoryDescriptor::new(Pages(1), Some(Pages(1)), false).unwrap();
|
|
|
|
let memory = memory::Memory::new(memory_descriptor).unwrap();
|
|
|
|
|
|
|
|
// test that basic access works and that len = 0 is caught correctly
|
|
|
|
let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
|
|
|
|
let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
|
|
|
|
|
|
|
|
assert!(start_wasm_ptr.deref(&memory).is_some());
|
|
|
|
assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
|
|
|
|
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_none());
|
|
|
|
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_none());
|
|
|
|
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_none() });
|
|
|
|
assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
|
|
|
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
|
|
|
|
|
|
|
// test that accessing the last valid memory address works correctly and OOB is caught
|
|
|
|
let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
|
|
|
|
let end_wasm_ptr: WasmPtr<u8> = WasmPtr::new(last_valid_address_for_u8);
|
|
|
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
|
|
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
|
|
|
|
|
|
|
let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
|
|
|
|
|
|
|
|
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
|
|
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
|
|
|
let invalid_idx_len_combos: [(u32, u32); 3] = [(0, 0), (0, 2), (1, 1)];
|
|
|
|
for &(idx, len) in invalid_idx_len_combos.into_iter() {
|
|
|
|
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
|
|
|
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
|
|
|
|
}
|
|
|
|
assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
|
|
|
|
|
|
|
|
// test that accesing the last valid memory address for a u32 is valid
|
|
|
|
// (same as above test but with more edge cases to assert on)
|
|
|
|
let last_valid_address_for_u32 = (memory.size().bytes().0 - 4) as u32;
|
|
|
|
let end_wasm_ptr: WasmPtr<u32> = WasmPtr::new(last_valid_address_for_u32);
|
|
|
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
|
|
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
|
|
|
assert!(end_wasm_ptr.deref(&memory).is_some());
|
|
|
|
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
|
|
|
|
|
|
|
|
let end_wasm_ptr_oob_array: [WasmPtr<u32>; 4] = [
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 1),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 2),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 3),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 4),
|
|
|
|
];
|
|
|
|
for oob_end_ptr in end_wasm_ptr_oob_array.into_iter() {
|
|
|
|
assert!(oob_end_ptr.deref(&memory).is_none());
|
|
|
|
assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
|
|
|
|
}
|
|
|
|
let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
|
|
|
|
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
|
|
|
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
|
|
|
|
|
|
|
|
let invalid_idx_len_combos: [(u32, u32); 4] = [(0, 0), (1, 0), (0, 2), (1, 1)];
|
|
|
|
for &(idx, len) in invalid_idx_len_combos.into_iter() {
|
|
|
|
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
|
|
|
|
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
|
|
|
|
}
|
|
|
|
|
|
|
|
let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 1),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 2),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 3),
|
|
|
|
WasmPtr::new(last_valid_address_for_u32 + 4),
|
|
|
|
];
|
|
|
|
|
|
|
|
for oob_end_array_ptr in end_wasm_ptr_array_oob_array.into_iter() {
|
|
|
|
assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
|
|
|
|
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
|
|
|
|
assert!(oob_end_array_ptr.deref(&memory, 0, 0).is_none());
|
|
|
|
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 0).is_none() });
|
|
|
|
assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
|
|
|
|
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|