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