1 #[cfg(all(not(target_os = "windows"), not(miri)))]
2 mod not_for_windows {
3     use wasmtime::*;
4 
5     use rustix::mm::{MapFlags, MprotectFlags, ProtFlags, mmap_anonymous, mprotect, munmap};
6 
7     use std::ptr::null_mut;
8     use std::sync::{Arc, Mutex};
9 
10     struct CustomMemory {
11         mem: usize,
12         size: usize,
13         guard_size: usize,
14         used_wasm_bytes: usize,
15         glob_bytes_counter: Arc<Mutex<usize>>,
16     }
17 
18     impl CustomMemory {
new(minimum: usize, maximum: usize, glob_counter: Arc<Mutex<usize>>) -> Self19         unsafe fn new(minimum: usize, maximum: usize, glob_counter: Arc<Mutex<usize>>) -> Self {
20             let page_size = rustix::param::page_size();
21             let guard_size = page_size;
22             let size = maximum + guard_size;
23             // We rely on the Wasm page size being multiple of host page size.
24             assert_eq!(size % page_size, 0);
25 
26             let mem = unsafe {
27                 mmap_anonymous(null_mut(), size, ProtFlags::empty(), MapFlags::PRIVATE)
28                     .expect("mmap failed")
29             };
30 
31             // NOTE: mmap_anonymous returns zero initialized memory, which is relied upon by this
32             // API.
33 
34             unsafe {
35                 mprotect(mem, minimum, MprotectFlags::READ | MprotectFlags::WRITE)
36                     .expect("mprotect failed");
37             }
38             *glob_counter.lock().unwrap() += minimum;
39 
40             Self {
41                 mem: mem as usize,
42                 size,
43                 guard_size,
44                 used_wasm_bytes: minimum,
45                 glob_bytes_counter: glob_counter,
46             }
47         }
48     }
49 
50     impl Drop for CustomMemory {
drop(&mut self)51         fn drop(&mut self) {
52             *self.glob_bytes_counter.lock().unwrap() -= self.used_wasm_bytes;
53             unsafe { munmap(self.mem as *mut _, self.size).expect("munmap failed") };
54         }
55     }
56 
57     unsafe impl LinearMemory for CustomMemory {
byte_size(&self) -> usize58         fn byte_size(&self) -> usize {
59             self.used_wasm_bytes
60         }
61 
byte_capacity(&self) -> usize62         fn byte_capacity(&self) -> usize {
63             self.size - self.guard_size
64         }
65 
grow_to(&mut self, new_size: usize) -> wasmtime::Result<()>66         fn grow_to(&mut self, new_size: usize) -> wasmtime::Result<()> {
67             println!("grow to {new_size:x}");
68             let delta = new_size - self.used_wasm_bytes;
69             unsafe {
70                 let start = (self.mem as *mut u8).add(self.used_wasm_bytes) as _;
71                 mprotect(start, delta, MprotectFlags::READ | MprotectFlags::WRITE)
72                     .expect("mprotect failed");
73             }
74 
75             *self.glob_bytes_counter.lock().unwrap() += delta;
76             self.used_wasm_bytes = new_size;
77             Ok(())
78         }
79 
as_ptr(&self) -> *mut u880         fn as_ptr(&self) -> *mut u8 {
81             self.mem as *mut u8
82         }
83     }
84 
85     struct CustomMemoryCreator {
86         pub num_created_memories: Mutex<usize>,
87         pub num_total_bytes: Arc<Mutex<usize>>,
88     }
89 
90     impl CustomMemoryCreator {
new() -> Self91         pub fn new() -> Self {
92             Self {
93                 num_created_memories: Mutex::new(0),
94                 num_total_bytes: Arc::new(Mutex::new(0)),
95             }
96         }
97     }
98 
99     unsafe impl MemoryCreator for CustomMemoryCreator {
new_memory( &self, ty: MemoryType, minimum: usize, maximum: Option<usize>, reserved_size: Option<usize>, guard_size: usize, ) -> Result<Box<dyn LinearMemory>, String>100         fn new_memory(
101             &self,
102             ty: MemoryType,
103             minimum: usize,
104             maximum: Option<usize>,
105             reserved_size: Option<usize>,
106             guard_size: usize,
107         ) -> Result<Box<dyn LinearMemory>, String> {
108             assert_eq!(guard_size, 0);
109             assert_eq!(reserved_size, Some(0));
110             assert!(!ty.is_64());
111             unsafe {
112                 // Cap the maximum at 10MiB to reduce the virtual memory
113                 // allocated by this test to execute on 32-bit platforms.
114                 let mem = Box::new(CustomMemory::new(
115                     minimum,
116                     maximum.unwrap_or(10 << 20),
117                     self.num_total_bytes.clone(),
118                 ));
119                 *self.num_created_memories.lock().unwrap() += 1;
120                 Ok(mem)
121             }
122         }
123     }
124 
config() -> (Store<()>, Arc<CustomMemoryCreator>)125     fn config() -> (Store<()>, Arc<CustomMemoryCreator>) {
126         let mem_creator = Arc::new(CustomMemoryCreator::new());
127         let mut config = Config::new();
128         config
129             .with_host_memory(mem_creator.clone())
130             .memory_reservation(0)
131             .memory_guard_size(0);
132         (Store::new(&Engine::new(&config).unwrap(), ()), mem_creator)
133     }
134 
135     #[test]
host_memory() -> wasmtime::Result<()>136     fn host_memory() -> wasmtime::Result<()> {
137         let (mut store, mem_creator) = config();
138         let module = Module::new(
139             store.engine(),
140             r#"
141             (module
142                 (memory (export "memory") 1)
143             )
144         "#,
145         )?;
146         Instance::new(&mut store, &module, &[])?;
147 
148         assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1);
149 
150         Ok(())
151     }
152 
153     #[test]
host_memory_grow() -> wasmtime::Result<()>154     fn host_memory_grow() -> wasmtime::Result<()> {
155         let (mut store, mem_creator) = config();
156         let module = Module::new(
157             store.engine(),
158             r#"
159             (module
160                 (func $f (drop (memory.grow (i32.const 1))))
161                 (memory (export "memory") 1 2)
162                 (start $f)
163             )
164         "#,
165         )?;
166 
167         Instance::new(&mut store, &module, &[])?;
168         let instance2 = Instance::new(&mut store, &module, &[])?;
169 
170         assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2);
171 
172         assert_eq!(
173             instance2
174                 .get_memory(&mut store, "memory")
175                 .unwrap()
176                 .size(&store),
177             2
178         );
179 
180         // we take the lock outside the assert, so it won't get poisoned on assert failure
181         let tot_pages = *mem_creator.num_total_bytes.lock().unwrap();
182         assert_eq!(
183             tot_pages,
184             (4 * wasmtime_environ::Memory::DEFAULT_PAGE_SIZE) as usize
185         );
186 
187         drop(store);
188         let tot_pages = *mem_creator.num_total_bytes.lock().unwrap();
189         assert_eq!(tot_pages, 0);
190 
191         Ok(())
192     }
193 }
194