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