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