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