xref: /wasmtime-44.0.1/tests/all/memory.rs (revision 0a55f804)
1 use rayon::prelude::*;
2 use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
3 use std::time::Duration;
4 use wasmtime::*;
5 use wasmtime_test_macros::wasmtime_test;
6 
module(engine: &Engine) -> Result<Module>7 fn module(engine: &Engine) -> Result<Module> {
8     let mut wat = format!("(module\n");
9     wat.push_str("(import \"\" \"\" (memory 0))\n");
10     for i in 0..=33 {
11         let offset = if i == 0 {
12             0
13         } else if i == 33 {
14             !0
15         } else {
16             1u32 << (i - 1)
17         };
18 
19         for (width, instr) in [
20             (1, &["i32.load8_s"][..]),
21             (2, &["i32.load16_s"]),
22             (4, &["i32.load" /*, "f32.load"*/]),
23             (8, &["i64.load" /*, "f64.load"*/]),
24             #[cfg(not(any(target_arch = "s390x", target_arch = "riscv64")))]
25             (16, &["v128.load"]),
26         ]
27         .iter()
28         {
29             for (j, instr) in instr.iter().enumerate() {
30                 wat.push_str(&format!(
31                     "(func (export \"{width} {offset} v{j}\") (param i32)\n"
32                 ));
33                 wat.push_str("local.get 0\n");
34                 wat.push_str(instr);
35                 wat.push_str(&format!(" offset={offset}\n"));
36                 wat.push_str("drop\n)");
37             }
38         }
39     }
40     wat.push_str(")");
41     Module::new(engine, &wat)
42 }
43 
44 struct TestFunc {
45     width: u32,
46     offset: u32,
47     func: TypedFunc<u32, ()>,
48 }
49 
find_funcs(store: &mut Store<()>, instance: &Instance) -> Vec<TestFunc>50 fn find_funcs(store: &mut Store<()>, instance: &Instance) -> Vec<TestFunc> {
51     let list = instance
52         .exports(&mut *store)
53         .map(|export| {
54             let name = export.name();
55             let mut parts = name.split_whitespace();
56             (
57                 parts.next().unwrap().parse().unwrap(),
58                 parts.next().unwrap().parse().unwrap(),
59                 export.into_func().unwrap(),
60             )
61         })
62         .collect::<Vec<_>>();
63     list.into_iter()
64         .map(|(width, offset, func)| TestFunc {
65             width,
66             offset,
67             func: func.typed(&store).unwrap(),
68         })
69         .collect()
70 }
71 
test_traps(store: &mut Store<()>, funcs: &[TestFunc], addr: u32, mem: &Memory)72 fn test_traps(store: &mut Store<()>, funcs: &[TestFunc], addr: u32, mem: &Memory) {
73     let mem_size = mem.data_size(&store) as u64;
74     for func in funcs {
75         let result = func.func.call(&mut *store, addr);
76         let base = u64::from(func.offset) + u64::from(addr);
77         let range = base..base + u64::from(func.width);
78         if range.start >= mem_size || range.end >= mem_size {
79             assert!(
80                 result.is_err(),
81                 "access at {}+{}+{} succeeded but should have failed when memory has {} bytes",
82                 addr,
83                 func.offset,
84                 func.width,
85                 mem_size
86             );
87         } else {
88             assert!(result.is_ok());
89         }
90     }
91 }
92 
93 #[wasmtime_test(wasm_features(simd))]
94 #[cfg_attr(miri, ignore)]
offsets_static_dynamic_oh_my(config: &mut Config) -> Result<()>95 fn offsets_static_dynamic_oh_my(config: &mut Config) -> Result<()> {
96     const GB: u64 = 1 << 30;
97     const MB: u64 = 1 << 20;
98 
99     let mut engines = Vec::new();
100     let sizes = if cfg!(target_pointer_width = "32") {
101         [0, 10 * MB, 20 * MB]
102     } else {
103         [0, 1 * GB, 4 * GB]
104     };
105     for &memory_reservation in sizes.iter() {
106         for &guard_size in sizes.iter() {
107             for &guard_before_linear_memory in [true, false].iter() {
108                 config.memory_reservation(memory_reservation);
109                 config.memory_guard_size(guard_size);
110                 config.guard_before_linear_memory(guard_before_linear_memory);
111                 config.cranelift_debug_verifier(true);
112                 engines.push(Engine::new(&config)?);
113             }
114         }
115     }
116 
117     engines.par_iter().try_for_each(|engine| {
118         let module = module(&engine)?;
119 
120         for (min, max) in [(1, Some(2)), (1, None)].iter() {
121             let mut store = Store::new(&engine, ());
122             let mem = Memory::new(&mut store, MemoryType::new(*min, *max)).unwrap();
123             let instance = Instance::new(&mut store, &module, &[mem.into()]).unwrap();
124             let funcs = find_funcs(&mut store, &instance);
125 
126             test_traps(&mut store, &funcs, 0, &mem);
127             test_traps(&mut store, &funcs, 65536, &mem);
128             test_traps(&mut store, &funcs, u32::MAX, &mem);
129 
130             mem.grow(&mut store, 1).unwrap();
131 
132             test_traps(&mut store, &funcs, 0, &mem);
133             test_traps(&mut store, &funcs, 65536, &mem);
134             test_traps(&mut store, &funcs, u32::MAX, &mem);
135         }
136         Ok::<_, wasmtime::Error>(())
137     })?;
138 
139     Ok(())
140 }
141 
142 #[test]
143 #[cfg_attr(miri, ignore)]
144 #[cfg_attr(asan, ignore)]
guards_present() -> Result<()>145 fn guards_present() -> Result<()> {
146     const GUARD_SIZE: u64 = 65536;
147 
148     let mut config = Config::new();
149     config.memory_reservation(1 << 20);
150     config.memory_guard_size(GUARD_SIZE);
151     config.guard_before_linear_memory(true);
152     let engine = Engine::new(&config)?;
153     let mut store = Store::new(&engine, ());
154     let static_mem = Memory::new(&mut store, MemoryType::new(1, Some(2)))?;
155     let dynamic_mem = Memory::new(&mut store, MemoryType::new(1, None))?;
156 
157     let assert_guards = |store: &Store<()>| unsafe {
158         // guards before
159         println!("check pre-static-mem");
160         assert_faults(static_mem.data_ptr(&store).offset(-(GUARD_SIZE as isize)));
161         println!("check pre-dynamic-mem");
162         assert_faults(dynamic_mem.data_ptr(&store).offset(-(GUARD_SIZE as isize)));
163 
164         // guards after
165         println!("check post-static-mem");
166         assert_faults(
167             static_mem
168                 .data_ptr(&store)
169                 .add(static_mem.data_size(&store)),
170         );
171         println!("check post-dynamic-mem");
172         assert_faults(
173             dynamic_mem
174                 .data_ptr(&store)
175                 .add(dynamic_mem.data_size(&store)),
176         );
177     };
178     assert_guards(&store);
179     // static memory should start with the second page unmapped
180     unsafe {
181         assert_faults(static_mem.data_ptr(&store).add(65536));
182     }
183     println!("growing");
184     static_mem.grow(&mut store, 1).unwrap();
185     dynamic_mem.grow(&mut store, 1).unwrap();
186     assert_guards(&store);
187 
188     Ok(())
189 }
190 
191 #[wasmtime_test]
192 #[cfg_attr(miri, ignore)]
193 #[cfg_attr(asan, ignore)]
guards_present_pooling(config: &mut Config) -> Result<()>194 fn guards_present_pooling(config: &mut Config) -> Result<()> {
195     const GUARD_SIZE: u64 = 65536;
196 
197     let mut pool = crate::small_pool_config();
198     pool.total_memories(2)
199         .max_memory_size(10 << 16)
200         .memory_protection_keys(Enabled::No);
201     config.memory_reservation(1 << 20);
202     config.memory_guard_size(GUARD_SIZE);
203     config.guard_before_linear_memory(true);
204     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
205     let engine = Engine::new(&config)?;
206 
207     let mut store = Store::new(&engine, ());
208 
209     let mem1 = {
210         let m = Module::new(&engine, "(module (memory (export \"\") 1 2))")?;
211         Instance::new(&mut store, &m, &[])?
212             .get_memory(&mut store, "")
213             .unwrap()
214     };
215     let mem2 = {
216         let m = Module::new(&engine, "(module (memory (export \"\") 1))")?;
217         Instance::new(&mut store, &m, &[])?
218             .get_memory(&mut store, "")
219             .unwrap()
220     };
221 
222     unsafe fn assert_guards(store: &Store<()>, mem: &Memory) {
223         unsafe {
224             // guards before
225             println!("check pre-mem");
226             assert_faults(mem.data_ptr(&store).offset(-(GUARD_SIZE as isize)));
227 
228             // unmapped just after memory
229             println!("check mem");
230             assert_faults(mem.data_ptr(&store).add(mem.data_size(&store)));
231 
232             // guards after memory
233             println!("check post-mem");
234             assert_faults(mem.data_ptr(&store).add(1 << 20));
235         }
236     }
237     unsafe {
238         assert_guards(&store, &mem1);
239         assert_guards(&store, &mem2);
240         println!("growing");
241         mem1.grow(&mut store, 1).unwrap();
242         mem2.grow(&mut store, 1).unwrap();
243         assert_guards(&store, &mem1);
244         assert_guards(&store, &mem2);
245     }
246 
247     Ok(())
248 }
249 
250 #[wasmtime_test]
251 #[cfg_attr(miri, ignore)]
252 #[cfg_attr(asan, ignore)]
253 #[cfg(target_arch = "x86_64")] // only platform with mpk
guards_present_pooling_mpk(config: &mut Config) -> Result<()>254 fn guards_present_pooling_mpk(config: &mut Config) -> Result<()> {
255     if !wasmtime::PoolingAllocationConfig::are_memory_protection_keys_available() {
256         println!("skipping `guards_present_pooling_mpk` test; mpk is not supported");
257         return Ok(());
258     }
259 
260     const GUARD_SIZE: u64 = 65536;
261     let mut pool = crate::small_pool_config();
262     pool.total_memories(4)
263         .max_memory_size(10 << 16)
264         .memory_protection_keys(Enabled::Yes)
265         .max_memory_protection_keys(2);
266     config.memory_reservation(1 << 20);
267     config.memory_guard_size(GUARD_SIZE);
268     config.guard_before_linear_memory(true);
269     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
270     let engine = Engine::new(&config)?;
271 
272     let mut store = Store::new(&engine, ());
273 
274     let mem1 = {
275         let m = Module::new(&engine, "(module (memory (export \"\") 1 2))")?;
276         Instance::new(&mut store, &m, &[])?
277             .get_memory(&mut store, "")
278             .unwrap()
279     };
280     let mem2 = {
281         let m = Module::new(&engine, "(module (memory (export \"\") 1))")?;
282         Instance::new(&mut store, &m, &[])?
283             .get_memory(&mut store, "")
284             .unwrap()
285     };
286 
287     unsafe fn assert_guards(store: &Store<()>, mem: &Memory) {
288         unsafe {
289             // guards before
290             println!("check pre-mem");
291             assert_faults(mem.data_ptr(&store).offset(-(GUARD_SIZE as isize)));
292 
293             // unmapped just after memory
294             println!("check mem");
295             assert_faults(mem.data_ptr(&store).add(mem.data_size(&store)));
296 
297             // guards after memory
298             println!("check post-mem");
299             assert_faults(mem.data_ptr(&store).add(1 << 20));
300         }
301     }
302     unsafe {
303         assert_guards(&store, &mem1);
304         assert_guards(&store, &mem2);
305         println!("growing");
306         mem1.grow(&mut store, 1).unwrap();
307         mem2.grow(&mut store, 1).unwrap();
308         assert_guards(&store, &mem1);
309         assert_guards(&store, &mem2);
310     }
311 
312     Ok(())
313 }
314 
assert_faults(ptr: *mut u8)315 unsafe fn assert_faults(ptr: *mut u8) {
316     use std::io::Error;
317     #[cfg(unix)]
318     unsafe {
319         // There's probably a faster way to do this here, but, uh, when in rome?
320         match libc::fork() {
321             0 => {
322                 *ptr = 4;
323 
324                 std::process::exit(0);
325             }
326             -1 => panic!("failed to fork: {}", Error::last_os_error()),
327             n => {
328                 let mut status = 0;
329                 assert!(
330                     libc::waitpid(n, &mut status, 0) == n,
331                     "failed to wait: {}",
332                     Error::last_os_error()
333                 );
334                 assert!(libc::WIFSIGNALED(status));
335             }
336         }
337     }
338     #[cfg(windows)]
339     unsafe {
340         use windows_sys::Win32::System::Memory::*;
341 
342         let mut info = std::mem::MaybeUninit::uninit();
343         let r = VirtualQuery(
344             ptr as *const _,
345             info.as_mut_ptr(),
346             std::mem::size_of_val(&info),
347         );
348         if r == 0 {
349             panic!("failed to VirtualAlloc: {}", Error::last_os_error());
350         }
351         let info = info.assume_init();
352         assert_eq!(info.AllocationProtect, PAGE_NOACCESS);
353     }
354 }
355 
356 // Disable test on s390x because the large allocation may actually succeed;
357 // the whole 64-bit address space is available on this platform.
358 #[test]
359 #[cfg(not(target_arch = "s390x"))]
massive_64_bit_still_limited() -> Result<()>360 fn massive_64_bit_still_limited() -> Result<()> {
361     // Creating a 64-bit memory which exceeds the limits of the address space
362     // should still send a request to the `ResourceLimiter` to ensure that it
363     // gets at least some chance to see that oom was requested.
364     let mut config = Config::new();
365     config.wasm_memory64(true);
366     let engine = Engine::new(&config)?;
367 
368     let mut store = Store::new(&engine, MyLimiter { hit: false });
369     store.limiter(|x| x);
370     let ty = MemoryType::new64(1 << 46, None);
371     assert!(Memory::new(&mut store, ty).is_err());
372     assert!(store.data().hit);
373 
374     return Ok(());
375 
376     struct MyLimiter {
377         hit: bool,
378     }
379 
380     impl ResourceLimiter for MyLimiter {
381         fn memory_growing(
382             &mut self,
383             _current: usize,
384             _request: usize,
385             _max: Option<usize>,
386         ) -> Result<bool> {
387             self.hit = true;
388             Ok(true)
389         }
390         fn table_growing(
391             &mut self,
392             _current: usize,
393             _request: usize,
394             _max: Option<usize>,
395         ) -> Result<bool> {
396             unreachable!()
397         }
398     }
399 }
400 
401 #[wasmtime_test]
402 #[cfg_attr(miri, ignore)]
tiny_static_heap(config: &mut Config) -> Result<()>403 fn tiny_static_heap(config: &mut Config) -> Result<()> {
404     // The size of the memory in the module below is the exact same size as
405     // the static memory size limit in the configuration. This is intended to
406     // specifically test that a load of all the valid addresses of the memory
407     // all pass bounds-checks in cranelift to help weed out any off-by-one bugs.
408     config.memory_reservation(1 << 16);
409     let engine = Engine::new(&config)?;
410     let mut store = Store::new(&engine, ());
411 
412     let module = Module::new(
413         &engine,
414         r#"
415             (module
416                 (memory 1 1)
417                 (func (export "run")
418                     (local $i i32)
419 
420                     (loop
421                         (if (i32.eq (local.get $i) (i32.const 65536))
422                             (then (return)))
423                         (drop (i32.load8_u (local.get $i)))
424                         (local.set $i (i32.add (local.get $i) (i32.const 1)))
425                         br 0
426                     )
427                 )
428             )
429         "#,
430     )?;
431 
432     let i = Instance::new(&mut store, &module, &[])?;
433     let f = i.get_typed_func::<(), ()>(&mut store, "run")?;
434     f.call(&mut store, ())?;
435     Ok(())
436 }
437 
438 #[test]
static_forced_max() -> Result<()>439 fn static_forced_max() -> Result<()> {
440     let mut config = Config::new();
441     config.memory_reservation(5 << 16);
442     config.memory_may_move(false);
443     let engine = Engine::new(&config)?;
444     let mut store = Store::new(&engine, ());
445 
446     let mem = Memory::new(&mut store, MemoryType::new(0, None))?;
447     mem.grow(&mut store, 5).unwrap();
448     assert!(mem.grow(&mut store, 1).is_err());
449     Ok(())
450 }
451 
452 #[wasmtime_test]
dynamic_extra_growth_unchanged_pointer(config: &mut Config) -> Result<()>453 fn dynamic_extra_growth_unchanged_pointer(config: &mut Config) -> Result<()> {
454     const EXTRA_PAGES: u64 = 5;
455     config.memory_reservation(0);
456     // 5 wasm pages extra
457     config.memory_reservation_for_growth(EXTRA_PAGES * (1 << 16));
458     let engine = Engine::new(&config)?;
459     let mut store = Store::new(&engine, ());
460 
461     fn assert_behaves_well(store: &mut Store<()>, mem: &Memory) -> Result<()> {
462         let ptr = mem.data_ptr(&store);
463 
464         // Each growth here should retain the same linear pointer in memory and the
465         // memory shouldn't get moved.
466         for _ in 0..EXTRA_PAGES {
467             mem.grow(&mut *store, 1)?;
468             assert_eq!(ptr, mem.data_ptr(&store));
469         }
470 
471         // Growth afterwards though will be forced to move the pointer
472         mem.grow(&mut *store, 1)?;
473         let new_ptr = mem.data_ptr(&store);
474         assert_ne!(ptr, new_ptr);
475 
476         for _ in 0..EXTRA_PAGES - 1 {
477             mem.grow(&mut *store, 1)?;
478             assert_eq!(new_ptr, mem.data_ptr(&store));
479         }
480         Ok(())
481     }
482 
483     let mem = Memory::new(&mut store, MemoryType::new(10, None))?;
484     assert_behaves_well(&mut store, &mem)?;
485 
486     let module = Module::new(&engine, r#"(module (memory (export "mem") 10))"#)?;
487     let instance = Instance::new(&mut store, &module, &[])?;
488     let mem = instance.get_memory(&mut store, "mem").unwrap();
489     assert_behaves_well(&mut store, &mem)?;
490 
491     let module = Module::new(
492         &engine,
493         r#"
494             (module
495                 (memory (export "mem") 10)
496                 (data (i32.const 0) ""))
497         "#,
498     )?;
499     let instance = Instance::new(&mut store, &module, &[])?;
500     let mem = instance.get_memory(&mut store, "mem").unwrap();
501     assert_behaves_well(&mut store, &mem)?;
502 
503     Ok(())
504 }
505 
506 // This test exercises trying to create memories of the maximum 64-bit memory
507 // size of `1 << 48` pages. This should always fail but in the process of
508 // determining this failure we shouldn't hit any overflows or anything like that
509 // (checked via debug-mode tests).
510 #[wasmtime_test]
memory64_maximum_minimum(config: &mut Config) -> Result<()>511 fn memory64_maximum_minimum(config: &mut Config) -> Result<()> {
512     config.wasm_memory64(true);
513     let engine = Engine::new(&config)?;
514     let mut store = Store::new(&engine, ());
515 
516     assert!(
517         MemoryTypeBuilder::default()
518             .memory64(true)
519             .min(1 << 48)
520             .build()
521             .is_err()
522     );
523 
524     let module = Module::new(
525         &engine,
526         format!(r#"(module (import "" "" (memory i64 {})))"#, 1u64 << 48),
527     )?;
528     let mem_ty = module
529         .imports()
530         .next()
531         .unwrap()
532         .ty()
533         .unwrap_memory()
534         .clone();
535     assert!(Memory::new(&mut store, mem_ty).is_err());
536 
537     let module = Module::new(
538         &engine,
539         &format!(
540             r#"
541                 (module
542                     (memory i64 {})
543                 )
544             "#,
545             1u64 << 48,
546         ),
547     )?;
548     assert!(Instance::new(&mut store, &module, &[]).is_err());
549 
550     let module = Module::new(
551         &engine,
552         &format!(
553             r#"
554                 (module
555                     (memory i64 {})
556                     (data (i64.const 0) "")
557                 )
558             "#,
559             1u64 << 48,
560         ),
561     )?;
562     assert!(Instance::new(&mut store, &module, &[]).is_err());
563 
564     Ok(())
565 }
566 
567 #[test]
shared_memory_basics() -> Result<()>568 fn shared_memory_basics() -> Result<()> {
569     let mut config = Config::new();
570     config.shared_memory(true);
571     let engine = Engine::new(&config)?;
572     assert!(SharedMemory::new(&engine, MemoryType::new(1, None)).is_err());
573     assert!(SharedMemory::new(&engine, MemoryType::new(1, Some(1))).is_err());
574     assert!(SharedMemory::new(&engine, MemoryType::new64(1, None)).is_err());
575     assert!(SharedMemory::new(&engine, MemoryType::new64(1, Some(1))).is_err());
576     assert!(
577         MemoryTypeBuilder::default()
578             .shared(true)
579             .min(1)
580             .max(Some(0))
581             .build()
582             .is_err()
583     );
584 
585     let memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?;
586     assert!(memory.ty().is_shared());
587     assert_eq!(memory.ty().minimum(), 1);
588     assert_eq!(memory.ty().maximum(), Some(1));
589     assert_eq!(memory.size(), 1);
590     assert_eq!(memory.data_size(), 65536);
591     assert_eq!(memory.data().len(), 65536);
592     assert!(memory.grow(1).is_err());
593 
594     // misaligned
595     assert_eq!(memory.atomic_notify(1, 100), Err(Trap::HeapMisaligned));
596     assert_eq!(
597         memory.atomic_wait32(1, 100, None),
598         Err(Trap::HeapMisaligned)
599     );
600     assert_eq!(
601         memory.atomic_wait64(1, 100, None),
602         Err(Trap::HeapMisaligned)
603     );
604 
605     // oob
606     assert_eq!(
607         memory.atomic_notify(1 << 20, 100),
608         Err(Trap::MemoryOutOfBounds)
609     );
610     assert_eq!(
611         memory.atomic_wait32(1 << 20, 100, None),
612         Err(Trap::MemoryOutOfBounds)
613     );
614     assert_eq!(
615         memory.atomic_wait64(1 << 20, 100, None),
616         Err(Trap::MemoryOutOfBounds)
617     );
618 
619     // ok
620     assert_eq!(memory.atomic_notify(8, 100), Ok(0));
621     assert_eq!(memory.atomic_wait32(8, 1, None), Ok(WaitResult::Mismatch));
622     assert_eq!(memory.atomic_wait64(8, 1, None), Ok(WaitResult::Mismatch));
623 
624     // timeout
625     let near_future = Duration::new(0, 100);
626     assert_eq!(
627         memory.atomic_wait32(8, 0, Some(near_future)),
628         Ok(WaitResult::TimedOut)
629     );
630     assert_eq!(
631         memory.atomic_wait64(8, 0, Some(near_future)),
632         Ok(WaitResult::TimedOut)
633     );
634 
635     Ok(())
636 }
637 
638 #[test]
639 #[cfg_attr(miri, ignore)]
shared_memory_wait_notify() -> Result<()>640 fn shared_memory_wait_notify() -> Result<()> {
641     const THREADS: usize = 8;
642     const COUNT: usize = 100_000;
643 
644     let mut config = Config::new();
645     config.shared_memory(true);
646     let engine = Engine::new(&config)?;
647     let memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?;
648     let data = unsafe { AtomicU32::from_ptr(memory.data().as_ptr().cast_mut().cast()) };
649     let locked = unsafe { AtomicU32::from_ptr(memory.data().as_ptr().add(4).cast_mut().cast()) };
650 
651     // Note that `SeqCst` is used here to not think much about the orderings
652     // here, and it also somewhat more closely mirrors what's happening in wasm.
653     let lock = || {
654         while locked.swap(1, SeqCst) == 1 {
655             memory.atomic_wait32(0, 1, None).unwrap();
656         }
657     };
658     let unlock = || {
659         locked.store(0, SeqCst);
660         memory.atomic_notify(0, 1).unwrap();
661     };
662 
663     std::thread::scope(|s| {
664         for _ in 0..THREADS {
665             s.spawn(|| {
666                 for _ in 0..COUNT {
667                     lock();
668                     let next = data.load(SeqCst) + 1;
669                     data.store(next, SeqCst);
670                     unlock();
671                 }
672             });
673         }
674     });
675 
676     assert_eq!(data.load(SeqCst), (THREADS * COUNT) as u32);
677 
678     Ok(())
679 }
680 
681 #[wasmtime_test]
682 #[cfg_attr(miri, ignore)]
683 #[cfg(target_pointer_width = "64")] // requires large VM reservation
init_with_negative_segment(cfg: &mut Config) -> Result<()>684 fn init_with_negative_segment(cfg: &mut Config) -> Result<()> {
685     let engine = Engine::new(cfg)?;
686     let module = Module::new(
687         &engine,
688         r#"
689             (module
690                 (memory 65536)
691                 (data (i32.const 0x8000_0000) "x")
692             )
693         "#,
694     )?;
695     let mut store = Store::new(&engine, ());
696     Instance::new(&mut store, &module, &[])?;
697     Ok(())
698 }
699 
700 #[test]
non_page_aligned_static_memory() -> Result<()>701 fn non_page_aligned_static_memory() -> Result<()> {
702     let mut config = Config::new();
703     config.memory_reservation(100_000);
704     config.memory_may_move(false);
705     let engine = Engine::new(&config)?;
706     let ty = MemoryType::new(1, None);
707     Memory::new(&mut Store::new(&engine, ()), ty)?;
708     Ok(())
709 }
710 
711 #[test]
new_memory_with_custom_page_size() -> Result<()>712 fn new_memory_with_custom_page_size() -> Result<()> {
713     let engine = Engine::default();
714     let mut store = Store::new(&engine, ());
715 
716     let ty = MemoryTypeBuilder::default()
717         .page_size_log2(0)
718         .min(4096)
719         .max(Some(9000))
720         .build()?;
721 
722     let mem = Memory::new(&mut store, ty)?;
723     assert_eq!(mem.data_size(&store), 4096);
724     assert_eq!(mem.size(&store), 4096);
725 
726     mem.grow(&mut store, 9000 - 4096)?;
727     assert_eq!(mem.data_size(&store), 9000);
728     assert_eq!(mem.size(&store), 9000);
729 
730     assert!(mem.grow(&mut store, 1).is_err());
731     assert_eq!(mem.data_size(&store), 9000);
732     assert_eq!(mem.size(&store), 9000);
733 
734     Ok(())
735 }
736 
737 #[wasmtime_test]
738 #[cfg_attr(miri, ignore)]
get_memory_type_with_custom_page_size_from_wasm(config: &mut Config) -> Result<()>739 fn get_memory_type_with_custom_page_size_from_wasm(config: &mut Config) -> Result<()> {
740     config.wasm_custom_page_sizes(true);
741     let engine = Engine::new(&config)?;
742     let mut store = Store::new(&engine, ());
743 
744     let module = Module::new(
745         &engine,
746         r#"
747             (module
748                 (memory (export "memory") 1 0xffffffff (pagesize 1))
749             )
750         "#,
751     )?;
752 
753     let instance = Instance::new(&mut store, &module, &[])?;
754     let memory = instance.get_memory(&mut store, "memory").unwrap();
755     let mem_ty = memory.ty(&store);
756 
757     assert_eq!(mem_ty.minimum(), 1);
758     assert_eq!(mem_ty.maximum(), Some(0xffffffff));
759     assert_eq!(mem_ty.page_size(), 1);
760     assert_eq!(mem_ty.page_size_log2(), 0);
761 
762     Ok(())
763 }
764 
765 #[wasmtime_test]
configure_zero(config: &mut Config) -> Result<()>766 fn configure_zero(config: &mut Config) -> Result<()> {
767     config.guard_before_linear_memory(false);
768     config.memory_guard_size(0);
769     config.memory_reservation(0);
770     config.memory_reservation_for_growth(0);
771     let engine = Engine::new(&config)?;
772     let mut store = Store::new(&engine, ());
773 
774     let ty = MemoryType::new(0, None);
775     let memory = Memory::new(&mut store, ty)?;
776     assert_eq!(memory.data_size(&store), 0);
777 
778     Ok(())
779 }
780