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