1 //! Generate various kinds of Wasm memory. 2 3 use anyhow::Result; 4 use arbitrary::{Arbitrary, Unstructured}; 5 use wasmtime::{LinearMemory, MemoryCreator, MemoryType}; 6 7 /// Configuration for linear memories in Wasmtime. 8 #[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)] 9 pub enum MemoryConfig { 10 /// Configuration for linear memories which correspond to normal 11 /// configuration settings in `wasmtime` itself. This will tweak various 12 /// parameters about static/dynamic memories. 13 Normal(NormalMemoryConfig), 14 15 /// Configuration to force use of a linear memory that's unaligned at its 16 /// base address to force all wasm addresses to be unaligned at the hardware 17 /// level, even if the wasm itself correctly aligns everything internally. 18 CustomUnaligned, 19 } 20 21 /// Represents a normal memory configuration for Wasmtime with the given 22 /// static and dynamic memory sizes. 23 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 24 #[allow(missing_docs)] 25 pub struct NormalMemoryConfig { 26 pub static_memory_maximum_size: Option<u64>, 27 pub static_memory_guard_size: Option<u64>, 28 pub dynamic_memory_guard_size: Option<u64>, 29 pub guard_before_linear_memory: bool, 30 } 31 32 impl<'a> Arbitrary<'a> for NormalMemoryConfig { 33 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 34 // This attempts to limit memory and guard sizes to 32-bit ranges so 35 // we don't exhaust a 64-bit address space easily. 36 let mut ret = Self { 37 static_memory_maximum_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into), 38 static_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into), 39 dynamic_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into), 40 guard_before_linear_memory: u.arbitrary()?, 41 }; 42 43 if let Some(dynamic) = ret.dynamic_memory_guard_size { 44 let statik = ret.static_memory_guard_size.unwrap_or(2 << 30); 45 ret.static_memory_guard_size = Some(statik.max(dynamic)); 46 } 47 Ok(ret) 48 } 49 } 50 51 /// A custom "linear memory allocator" for wasm which only works with the 52 /// "dynamic" mode of configuration where wasm always does explicit bounds 53 /// checks. 54 /// 55 /// This memory attempts to always use unaligned host addresses for the base 56 /// address of linear memory with wasm. This means that all jit loads/stores 57 /// should be unaligned, which is a "big hammer way" of testing that all our JIT 58 /// code works with unaligned addresses since alignment is not required for 59 /// correctness in wasm itself. 60 pub struct UnalignedMemory { 61 /// This memory is always one byte larger than the actual size of linear 62 /// memory. 63 src: Vec<u8>, 64 maximum: Option<usize>, 65 } 66 67 unsafe impl LinearMemory for UnalignedMemory { 68 fn byte_size(&self) -> usize { 69 // Chop off the extra byte reserved for the true byte size of this 70 // linear memory. 71 self.src.len() - 1 72 } 73 74 fn maximum_byte_size(&self) -> Option<usize> { 75 self.maximum 76 } 77 78 fn grow_to(&mut self, new_size: usize) -> Result<()> { 79 // Make sure to allocate an extra byte for our "unalignment" 80 self.src.resize(new_size + 1, 0); 81 Ok(()) 82 } 83 84 fn as_ptr(&self) -> *mut u8 { 85 // Return our allocated memory, offset by one, so that the base address 86 // of memory is always unaligned. 87 self.src[1..].as_ptr() as *mut _ 88 } 89 } 90 91 /// A mechanism to generate [`UnalignedMemory`] at runtime. 92 pub struct UnalignedMemoryCreator; 93 94 unsafe impl MemoryCreator for UnalignedMemoryCreator { 95 fn new_memory( 96 &self, 97 _ty: MemoryType, 98 minimum: usize, 99 maximum: Option<usize>, 100 reserved_size_in_bytes: Option<usize>, 101 guard_size_in_bytes: usize, 102 ) -> Result<Box<dyn LinearMemory>, String> { 103 assert_eq!(guard_size_in_bytes, 0); 104 assert!(reserved_size_in_bytes.is_none() || reserved_size_in_bytes == Some(0)); 105 Ok(Box::new(UnalignedMemory { 106 src: vec![0; minimum + 1], 107 maximum, 108 })) 109 } 110 } 111