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