1 use super::{skip_pooling_allocator_tests, ErrorExt};
2 use wasmtime::*;
3 
4 #[test]
5 fn successful_instantiation() -> Result<()> {
6     let pool = crate::small_pool_config();
7     let mut config = Config::new();
8     config.allocation_strategy(pool);
9     config.memory_guard_size(0);
10     config.memory_reservation(1 << 16);
11 
12     let engine = Engine::new(&config)?;
13     let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?;
14 
15     // Module should instantiate
16     let mut store = Store::new(&engine, ());
17     Instance::new(&mut store, &module, &[])?;
18 
19     Ok(())
20 }
21 
22 #[test]
23 #[cfg_attr(miri, ignore)]
24 fn memory_limit() -> Result<()> {
25     let mut pool = crate::small_pool_config();
26     pool.max_memory_size(3 << 16);
27     let mut config = Config::new();
28     config.allocation_strategy(pool);
29     config.memory_guard_size(1 << 16);
30     config.memory_reservation(3 << 16);
31     config.wasm_multi_memory(true);
32 
33     let engine = Engine::new(&config)?;
34 
35     // Module should fail to instantiate because it has too many memories
36     match Module::new(&engine, r#"(module (memory 1) (memory 1))"#) {
37         Ok(_) => panic!("module instantiation should fail"),
38         Err(e) => {
39             e.assert_contains("defined memories count of 2 exceeds the per-instance limit of 1")
40         }
41     }
42 
43     // Module should fail to instantiate because the minimum is greater than
44     // the configured limit
45     match Module::new(&engine, r#"(module (memory 4))"#) {
46         Ok(_) => panic!("module instantiation should fail"),
47         Err(e) =>
48             e.assert_contains(
49             "memory index 0 has a minimum byte size of 262144 which exceeds the limit of 0x30000 bytes",
50         ),
51     }
52 
53     let module = Module::new(
54         &engine,
55         r#"(module (memory (export "m") 0) (func (export "f") (result i32) (memory.grow (i32.const 1))))"#,
56     )?;
57 
58     // Instantiate the module and grow the memory via the `f` function
59     {
60         let mut store = Store::new(&engine, ());
61         let instance = Instance::new(&mut store, &module, &[])?;
62         let f = instance.get_typed_func::<(), i32>(&mut store, "f")?;
63 
64         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 0);
65         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 1);
66         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 2);
67         assert_eq!(
68             f.call(&mut store, ()).expect("function should not trap"),
69             -1
70         );
71         assert_eq!(
72             f.call(&mut store, ()).expect("function should not trap"),
73             -1
74         );
75     }
76 
77     // Instantiate the module and grow the memory via the Wasmtime API
78     let mut store = Store::new(&engine, ());
79     let instance = Instance::new(&mut store, &module, &[])?;
80 
81     let memory = instance.get_memory(&mut store, "m").unwrap();
82     assert_eq!(memory.size(&store), 0);
83     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 0);
84     assert_eq!(memory.size(&store), 1);
85     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 1);
86     assert_eq!(memory.size(&store), 2);
87     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 2);
88     assert_eq!(memory.size(&store), 3);
89     assert!(memory.grow(&mut store, 1).is_err());
90 
91     Ok(())
92 }
93 
94 #[test]
95 fn memory_init() -> Result<()> {
96     let mut pool = crate::small_pool_config();
97     pool.max_memory_size(2 << 16).table_elements(0);
98     let mut config = Config::new();
99     config.allocation_strategy(pool);
100 
101     let engine = Engine::new(&config)?;
102 
103     let module = Module::new(
104         &engine,
105         r#"
106             (module
107                 (memory (export "m") 2)
108                 (data (i32.const 65530) "this data spans multiple pages")
109                 (data (i32.const 10) "hello world")
110             )
111         "#,
112     )?;
113 
114     let mut store = Store::new(&engine, ());
115     let instance = Instance::new(&mut store, &module, &[])?;
116     let memory = instance.get_memory(&mut store, "m").unwrap();
117 
118     assert_eq!(
119         &memory.data(&store)[65530..65560],
120         b"this data spans multiple pages"
121     );
122     assert_eq!(&memory.data(&store)[10..21], b"hello world");
123 
124     Ok(())
125 }
126 
127 #[test]
128 #[cfg_attr(miri, ignore)]
129 fn memory_guard_page_trap() -> Result<()> {
130     let mut pool = crate::small_pool_config();
131     pool.max_memory_size(2 << 16).table_elements(0);
132     let mut config = Config::new();
133     config.allocation_strategy(pool);
134 
135     let engine = Engine::new(&config)?;
136 
137     let module = Module::new(
138         &engine,
139         r#"
140             (module
141                 (memory (export "m") 0)
142                 (func (export "f") (param i32) local.get 0 i32.load drop)
143             )
144         "#,
145     )?;
146 
147     // Instantiate the module and check for out of bounds trap
148     for _ in 0..10 {
149         let mut store = Store::new(&engine, ());
150         let instance = Instance::new(&mut store, &module, &[])?;
151         let m = instance.get_memory(&mut store, "m").unwrap();
152         let f = instance.get_typed_func::<i32, ()>(&mut store, "f")?;
153 
154         let trap = f
155             .call(&mut store, 0)
156             .expect_err("function should trap")
157             .downcast::<Trap>()?;
158         assert_eq!(trap, Trap::MemoryOutOfBounds);
159 
160         let trap = f
161             .call(&mut store, 1)
162             .expect_err("function should trap")
163             .downcast::<Trap>()?;
164         assert_eq!(trap, Trap::MemoryOutOfBounds);
165 
166         m.grow(&mut store, 1).expect("memory should grow");
167         f.call(&mut store, 0).expect("function should not trap");
168 
169         let trap = f
170             .call(&mut store, 65536)
171             .expect_err("function should trap")
172             .downcast::<Trap>()?;
173         assert_eq!(trap, Trap::MemoryOutOfBounds);
174 
175         let trap = f
176             .call(&mut store, 65537)
177             .expect_err("function should trap")
178             .downcast::<Trap>()?;
179         assert_eq!(trap, Trap::MemoryOutOfBounds);
180 
181         m.grow(&mut store, 1).expect("memory should grow");
182         f.call(&mut store, 65536).expect("function should not trap");
183 
184         m.grow(&mut store, 1)
185             .expect_err("memory should be at the limit");
186     }
187 
188     Ok(())
189 }
190 
191 #[test]
192 fn memory_zeroed() -> Result<()> {
193     if skip_pooling_allocator_tests() {
194         return Ok(());
195     }
196 
197     let mut pool = crate::small_pool_config();
198     pool.max_memory_size(1 << 16).table_elements(0);
199     let mut config = Config::new();
200     config.allocation_strategy(pool);
201     config.memory_guard_size(0);
202     config.memory_reservation(1 << 16);
203 
204     let engine = Engine::new(&config)?;
205 
206     let module = Module::new(&engine, r#"(module (memory (export "m") 1))"#)?;
207 
208     // Instantiate the module repeatedly after writing data to the entire memory
209     for _ in 0..10 {
210         let mut store = Store::new(&engine, ());
211         let instance = Instance::new(&mut store, &module, &[])?;
212         let memory = instance.get_memory(&mut store, "m").unwrap();
213 
214         assert_eq!(memory.size(&store,), 1);
215         assert_eq!(memory.data_size(&store), 65536);
216 
217         let ptr = memory.data_mut(&mut store).as_mut_ptr();
218 
219         unsafe {
220             for i in 0..8192 {
221                 assert_eq!(*ptr.cast::<u64>().offset(i), 0);
222             }
223             std::ptr::write_bytes(ptr, 0xFE, memory.data_size(&store));
224         }
225     }
226 
227     Ok(())
228 }
229 
230 #[test]
231 #[cfg_attr(miri, ignore)]
232 fn table_limit() -> Result<()> {
233     const TABLE_ELEMENTS: usize = 10;
234     let mut pool = crate::small_pool_config();
235     pool.table_elements(TABLE_ELEMENTS);
236     let mut config = Config::new();
237     config.allocation_strategy(pool);
238     config.memory_guard_size(0);
239     config.memory_reservation(1 << 16);
240 
241     let engine = Engine::new(&config)?;
242 
243     // Module should fail to instantiate because it has too many tables
244     match Module::new(&engine, r#"(module (table 1 funcref) (table 1 funcref))"#) {
245         Ok(_) => panic!("module compilation should fail"),
246         Err(e) => {
247             e.assert_contains("defined tables count of 2 exceeds the per-instance limit of 1")
248         }
249     }
250 
251     // Module should fail to instantiate because the minimum is greater than
252     // the configured limit
253     match Module::new(&engine, r#"(module (table 31 funcref))"#) {
254         Ok(_) => panic!("module compilation should fail"),
255         Err(e) => e.assert_contains(
256             "table index 0 has a minimum element size of 31 which exceeds the limit of 10",
257         ),
258     }
259 
260     let module = Module::new(
261         &engine,
262         r#"(module (table (export "t") 0 funcref) (func (export "f") (result i32) (table.grow (ref.null func) (i32.const 1))))"#,
263     )?;
264 
265     // Instantiate the module and grow the table via the `f` function
266     {
267         let mut store = Store::new(&engine, ());
268         let instance = Instance::new(&mut store, &module, &[])?;
269         let f = instance.get_typed_func::<(), i32>(&mut store, "f")?;
270 
271         for i in 0..TABLE_ELEMENTS {
272             assert_eq!(
273                 f.call(&mut store, ()).expect("function should not trap"),
274                 i as i32
275             );
276         }
277 
278         assert_eq!(
279             f.call(&mut store, ()).expect("function should not trap"),
280             -1
281         );
282         assert_eq!(
283             f.call(&mut store, ()).expect("function should not trap"),
284             -1
285         );
286     }
287 
288     // Instantiate the module and grow the table via the Wasmtime API
289     let mut store = Store::new(&engine, ());
290     let instance = Instance::new(&mut store, &module, &[])?;
291 
292     let table = instance.get_table(&mut store, "t").unwrap();
293 
294     for i in 0..TABLE_ELEMENTS {
295         assert_eq!(table.size(&store), i as u64);
296         assert_eq!(
297             table
298                 .grow(&mut store, 1, Ref::Func(None))
299                 .expect("table should grow"),
300             i as u64
301         );
302     }
303 
304     assert_eq!(table.size(&store), TABLE_ELEMENTS as u64);
305     assert!(table.grow(&mut store, 1, Ref::Func(None)).is_err());
306 
307     Ok(())
308 }
309 
310 #[test]
311 #[cfg_attr(miri, ignore)]
312 fn table_init() -> Result<()> {
313     let mut pool = crate::small_pool_config();
314     pool.max_memory_size(0).table_elements(6);
315     let mut config = Config::new();
316     config.allocation_strategy(pool);
317 
318     let engine = Engine::new(&config)?;
319 
320     let module = Module::new(
321         &engine,
322         r#"
323             (module
324                 (table (export "t") 6 funcref)
325                 (elem (i32.const 1) 1 2 3 4)
326                 (elem (i32.const 0) 0)
327                 (func)
328                 (func (param i32))
329                 (func (param i32 i32))
330                 (func (param i32 i32 i32))
331                 (func (param i32 i32 i32 i32))
332             )
333         "#,
334     )?;
335 
336     let mut store = Store::new(&engine, ());
337     let instance = Instance::new(&mut store, &module, &[])?;
338     let table = instance.get_table(&mut store, "t").unwrap();
339 
340     for i in 0..5 {
341         let v = table.get(&mut store, i).expect("table should have entry");
342         let f = v
343             .as_func()
344             .expect("expected funcref")
345             .expect("expected non-null value");
346         assert_eq!(f.ty(&store).params().len(), i as usize);
347     }
348 
349     assert!(
350         table
351             .get(&mut store, 5)
352             .expect("table should have entry")
353             .as_func()
354             .expect("expected funcref")
355             .is_none(),
356         "funcref should be null"
357     );
358 
359     Ok(())
360 }
361 
362 #[test]
363 fn table_zeroed() -> Result<()> {
364     if skip_pooling_allocator_tests() {
365         return Ok(());
366     }
367 
368     let pool = crate::small_pool_config();
369     let mut config = Config::new();
370     config.allocation_strategy(pool);
371     config.memory_guard_size(0);
372     config.memory_reservation(1 << 16);
373 
374     let engine = Engine::new(&config)?;
375 
376     let module = Module::new(&engine, r#"(module (table (export "t") 10 funcref))"#)?;
377 
378     // Instantiate the module repeatedly after filling table elements
379     for _ in 0..10 {
380         let mut store = Store::new(&engine, ());
381         let instance = Instance::new(&mut store, &module, &[])?;
382         let table = instance.get_table(&mut store, "t").unwrap();
383         let f = Func::wrap(&mut store, || {});
384 
385         assert_eq!(table.size(&store), 10);
386 
387         for i in 0..10 {
388             match table.get(&mut store, i).unwrap() {
389                 Ref::Func(r) => assert!(r.is_none()),
390                 _ => panic!("expected a funcref"),
391             }
392             table.set(&mut store, i, Ref::Func(Some(f))).unwrap();
393         }
394     }
395 
396     Ok(())
397 }
398 
399 #[test]
400 fn total_core_instances_limit() -> Result<()> {
401     const INSTANCE_LIMIT: u32 = 10;
402     let mut pool = crate::small_pool_config();
403     pool.total_core_instances(INSTANCE_LIMIT);
404     let mut config = Config::new();
405     config.allocation_strategy(pool);
406     config.memory_guard_size(0);
407     config.memory_reservation(1 << 16);
408 
409     let engine = Engine::new(&config)?;
410     let module = Module::new(&engine, r#"(module)"#)?;
411 
412     // Instantiate to the limit
413     {
414         let mut store = Store::new(&engine, ());
415 
416         for _ in 0..INSTANCE_LIMIT {
417             Instance::new(&mut store, &module, &[])?;
418         }
419 
420         match Instance::new(&mut store, &module, &[]) {
421             Ok(_) => panic!("instantiation should fail"),
422             Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
423         }
424     }
425 
426     // With the above store dropped, ensure instantiations can be made
427 
428     let mut store = Store::new(&engine, ());
429 
430     for _ in 0..INSTANCE_LIMIT {
431         Instance::new(&mut store, &module, &[])?;
432     }
433 
434     Ok(())
435 }
436 
437 #[test]
438 fn preserve_data_segments() -> Result<()> {
439     let mut pool = crate::small_pool_config();
440     pool.total_memories(2);
441     let mut config = Config::new();
442     config.allocation_strategy(pool);
443     let engine = Engine::new(&config)?;
444     let m = Module::new(
445         &engine,
446         r#"
447             (module
448                 (memory (export "mem") 1 1)
449                 (data (i32.const 0) "foo"))
450         "#,
451     )?;
452     let mut store = Store::new(&engine, ());
453     let i = Instance::new(&mut store, &m, &[])?;
454 
455     // Drop the module. This should *not* drop the actual data referenced by the
456     // module.
457     drop(m);
458 
459     // Spray some stuff on the heap. If wasm data lived on the heap this should
460     // paper over things and help us catch use-after-free here if it would
461     // otherwise happen.
462     if !cfg!(miri) {
463         let mut strings = Vec::new();
464         for _ in 0..1000 {
465             let mut string = String::new();
466             for _ in 0..1000 {
467                 string.push('g');
468             }
469             strings.push(string);
470         }
471         drop(strings);
472     }
473 
474     let mem = i.get_memory(&mut store, "mem").unwrap();
475 
476     // Hopefully it's still `foo`!
477     assert!(mem.data(&store).starts_with(b"foo"));
478 
479     Ok(())
480 }
481 
482 #[test]
483 fn multi_memory_with_imported_memories() -> Result<()> {
484     // This test checks that the base address for the defined memory is correct for the instance
485     // despite the presence of an imported memory.
486 
487     let mut pool = crate::small_pool_config();
488     pool.total_memories(2).max_memories_per_module(2);
489     let mut config = Config::new();
490     config.allocation_strategy(pool);
491     config.wasm_multi_memory(true);
492 
493     let engine = Engine::new(&config)?;
494     let module = Module::new(
495         &engine,
496         r#"(module (import "" "m1" (memory 0)) (memory (export "m2") 1))"#,
497     )?;
498 
499     let mut store = Store::new(&engine, ());
500 
501     let m1 = Memory::new(&mut store, MemoryType::new(0, None))?;
502     let instance = Instance::new(&mut store, &module, &[m1.into()])?;
503 
504     let m2 = instance.get_memory(&mut store, "m2").unwrap();
505 
506     m2.data_mut(&mut store)[0] = 0x42;
507     assert_eq!(m2.data(&store)[0], 0x42);
508 
509     Ok(())
510 }
511 
512 #[test]
513 fn drop_externref_global_during_module_init() -> Result<()> {
514     struct Limiter;
515 
516     impl ResourceLimiter for Limiter {
517         fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> {
518             Ok(false)
519         }
520 
521         fn table_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> {
522             Ok(false)
523         }
524     }
525 
526     let pool = crate::small_pool_config();
527     let mut config = Config::new();
528     config.wasm_reference_types(true);
529     config.allocation_strategy(pool);
530 
531     let engine = Engine::new(&config)?;
532 
533     let module = Module::new(
534         &engine,
535         r#"
536             (module
537                 (global i32 (i32.const 1))
538                 (global i32 (i32.const 2))
539                 (global i32 (i32.const 3))
540                 (global i32 (i32.const 4))
541                 (global i32 (i32.const 5))
542             )
543         "#,
544     )?;
545 
546     let mut store = Store::new(&engine, Limiter);
547     Instance::new(&mut store, &module, &[])?;
548     drop(store);
549 
550     let module = Module::new(
551         &engine,
552         r#"
553             (module
554                 (memory 1)
555                 (global (mut externref) (ref.null extern))
556             )
557         "#,
558     )?;
559 
560     let mut store = Store::new(&engine, Limiter);
561     store.limiter(|s| s);
562     assert!(Instance::new(&mut store, &module, &[]).is_err());
563 
564     Ok(())
565 }
566 
567 #[test]
568 #[cfg_attr(miri, ignore)]
569 fn switch_image_and_non_image() -> Result<()> {
570     let pool = crate::small_pool_config();
571     let mut c = Config::new();
572     c.allocation_strategy(pool);
573     let engine = Engine::new(&c)?;
574     let module1 = Module::new(
575         &engine,
576         r#"
577             (module
578                 (memory 1)
579                 (func (export "load") (param i32) (result i32)
580                     local.get 0
581                     i32.load
582                 )
583             )
584         "#,
585     )?;
586     let module2 = Module::new(
587         &engine,
588         r#"
589             (module
590                 (memory (export "memory") 1)
591                 (data (i32.const 0) "1234")
592             )
593         "#,
594     )?;
595 
596     let assert_zero = || -> Result<()> {
597         let mut store = Store::new(&engine, ());
598         let instance = Instance::new(&mut store, &module1, &[])?;
599         let func = instance.get_typed_func::<i32, i32>(&mut store, "load")?;
600         assert_eq!(func.call(&mut store, 0)?, 0);
601         Ok(())
602     };
603 
604     // Initialize with a heap image and make sure the next instance, without an
605     // image, is zeroed
606     Instance::new(&mut Store::new(&engine, ()), &module2, &[])?;
607     assert_zero()?;
608 
609     // ... transition back to heap image and do this again
610     Instance::new(&mut Store::new(&engine, ()), &module2, &[])?;
611     assert_zero()?;
612 
613     // And go back to an image and make sure it's read/write on the host.
614     let mut store = Store::new(&engine, ());
615     let instance = Instance::new(&mut store, &module2, &[])?;
616     let memory = instance.get_memory(&mut store, "memory").unwrap();
617     let mem = memory.data_mut(&mut store);
618     assert!(mem.starts_with(b"1234"));
619     mem[..6].copy_from_slice(b"567890");
620 
621     Ok(())
622 }
623 
624 #[test]
625 #[cfg(target_pointer_width = "64")]
626 #[cfg_attr(miri, ignore)]
627 fn instance_too_large() -> Result<()> {
628     let mut pool = crate::small_pool_config();
629     pool.max_core_instance_size(16);
630     let mut config = Config::new();
631     config.allocation_strategy(pool);
632 
633     let engine = Engine::new(&config)?;
634     match Module::new(&engine, "(module)") {
635         Ok(_) => panic!("should have failed to compile"),
636         Err(e) => {
637             e.assert_contains("exceeds the configured maximum of 16 bytes");
638             e.assert_contains("breakdown of allocation requirement");
639             e.assert_contains("instance state management");
640             e.assert_contains("static vmctx data");
641         }
642     }
643 
644     let mut lots_of_globals = format!("(module");
645     for _ in 0..100 {
646         lots_of_globals.push_str("(global i32 i32.const 0)\n");
647     }
648     lots_of_globals.push_str(")");
649 
650     match Module::new(&engine, &lots_of_globals) {
651         Ok(_) => panic!("should have failed to compile"),
652         Err(e) => {
653             e.assert_contains("exceeds the configured maximum of 16 bytes");
654             e.assert_contains("breakdown of allocation requirement");
655             e.assert_contains("defined globals");
656             e.assert_contains("instance state management");
657         }
658     }
659 
660     Ok(())
661 }
662 
663 #[test]
664 #[cfg_attr(miri, ignore)]
665 fn dynamic_memory_pooling_allocator() -> Result<()> {
666     for guard_size in [0, 1 << 16] {
667         let max_size = 128 << 20;
668         let mut pool = crate::small_pool_config();
669         pool.max_memory_size(max_size as usize);
670         let mut config = Config::new();
671         config.memory_reservation(max_size);
672         config.memory_guard_size(guard_size);
673         config.allocation_strategy(pool);
674 
675         let engine = Engine::new(&config)?;
676 
677         let module = Module::new(
678             &engine,
679             r#"
680             (module
681                 (memory (export "memory") 1)
682 
683                 (func (export "grow") (param i32) (result i32)
684                     local.get 0
685                     memory.grow)
686 
687                 (func (export "size") (result i32)
688                     memory.size)
689 
690                 (func (export "i32.load") (param i32) (result i32)
691                     local.get 0
692                     i32.load)
693 
694                 (func (export "i32.store") (param i32 i32)
695                     local.get 0
696                     local.get 1
697                     i32.store)
698 
699                 (data (i32.const 100) "x")
700             )
701          "#,
702         )?;
703 
704         let mut store = Store::new(&engine, ());
705         let instance = Instance::new(&mut store, &module, &[])?;
706 
707         let grow = instance.get_typed_func::<u32, i32>(&mut store, "grow")?;
708         let size = instance.get_typed_func::<(), u32>(&mut store, "size")?;
709         let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
710         let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?;
711         let memory = instance.get_memory(&mut store, "memory").unwrap();
712 
713         // basic length 1 tests
714         // assert_eq!(memory.grow(&mut store, 1)?, 0);
715         assert_eq!(memory.size(&store), 1);
716         assert_eq!(size.call(&mut store, ())?, 1);
717         assert_eq!(i32_load.call(&mut store, 0)?, 0);
718         assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x'));
719         i32_store.call(&mut store, (0, 0))?;
720         i32_store.call(&mut store, (100, i32::from(b'y')))?;
721         assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y'));
722 
723         // basic length 2 tests
724         let page = 64 * 1024;
725         assert_eq!(grow.call(&mut store, 1)?, 1);
726         assert_eq!(memory.size(&store), 2);
727         assert_eq!(size.call(&mut store, ())?, 2);
728         i32_store.call(&mut store, (page, 200))?;
729         assert_eq!(i32_load.call(&mut store, page)?, 200);
730 
731         // test writes are visible
732         i32_store.call(&mut store, (2, 100))?;
733         assert_eq!(i32_load.call(&mut store, 2)?, 100);
734 
735         // test growth can't exceed maximum
736         let too_many = max_size / (64 * 1024);
737         assert_eq!(grow.call(&mut store, too_many as u32)?, -1);
738         assert!(memory.grow(&mut store, too_many).is_err());
739 
740         assert_eq!(memory.data(&store)[page as usize], 200);
741 
742         // Re-instantiate in another store.
743         store = Store::new(&engine, ());
744         let instance = Instance::new(&mut store, &module, &[])?;
745         let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
746         let memory = instance.get_memory(&mut store, "memory").unwrap();
747 
748         // This is out of bounds...
749         assert!(i32_load.call(&mut store, page).is_err());
750         assert_eq!(memory.data_size(&store), page as usize);
751 
752         // ... but implementation-wise it should still be mapped memory from
753         // before if we don't have any guard pages.
754         //
755         // Note though that prior writes should all appear as zeros and we can't see
756         // data from the prior instance.
757         //
758         // Note that this part is only implemented on Linux which has
759         // `MADV_DONTNEED`.
760         if cfg!(target_os = "linux") && guard_size == 0 {
761             unsafe {
762                 let ptr = memory.data_ptr(&store);
763                 assert_eq!(*ptr.offset(page as isize), 0);
764             }
765         }
766     }
767 
768     Ok(())
769 }
770 
771 #[test]
772 #[cfg_attr(miri, ignore)]
773 fn zero_memory_pages_disallows_oob() -> Result<()> {
774     let mut pool = crate::small_pool_config();
775     pool.max_memory_size(0);
776     let mut config = Config::new();
777     config.allocation_strategy(pool);
778 
779     let engine = Engine::new(&config)?;
780     let module = Module::new(
781         &engine,
782         r#"
783             (module
784                 (memory 0)
785 
786                 (func (export "load") (param i32) (result i32)
787                     local.get 0
788                     i32.load)
789 
790                 (func (export "store") (param i32 )
791                     local.get 0
792                     local.get 0
793                     i32.store)
794             )
795         "#,
796     )?;
797     let mut store = Store::new(&engine, ());
798     let instance = Instance::new(&mut store, &module, &[])?;
799     let load32 = instance.get_typed_func::<i32, i32>(&mut store, "load")?;
800     let store32 = instance.get_typed_func::<i32, ()>(&mut store, "store")?;
801     for i in 0..31 {
802         assert!(load32.call(&mut store, 1 << i).is_err());
803         assert!(store32.call(&mut store, 1 << i).is_err());
804     }
805     Ok(())
806 }
807 
808 #[test]
809 #[cfg(feature = "component-model")]
810 fn total_component_instances_limit() -> Result<()> {
811     const TOTAL_COMPONENT_INSTANCES: u32 = 5;
812 
813     let mut pool = crate::small_pool_config();
814     pool.total_component_instances(TOTAL_COMPONENT_INSTANCES);
815     let mut config = Config::new();
816     config.wasm_component_model(true);
817     config.allocation_strategy(pool);
818 
819     let engine = Engine::new(&config)?;
820     let linker = wasmtime::component::Linker::new(&engine);
821     let component = wasmtime::component::Component::new(&engine, "(component)")?;
822 
823     let mut store = Store::new(&engine, ());
824     for _ in 0..TOTAL_COMPONENT_INSTANCES {
825         linker.instantiate(&mut store, &component)?;
826     }
827 
828     match linker.instantiate(&mut store, &component) {
829         Ok(_) => panic!("should have hit component instance limit"),
830         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
831     }
832 
833     drop(store);
834     let mut store = Store::new(&engine, ());
835     for _ in 0..TOTAL_COMPONENT_INSTANCES {
836         linker.instantiate(&mut store, &component)?;
837     }
838 
839     Ok(())
840 }
841 
842 #[test]
843 #[cfg(feature = "component-model")]
844 #[cfg(target_pointer_width = "64")] // error message tailored for 64-bit
845 fn component_instance_size_limit() -> Result<()> {
846     let mut pool = crate::small_pool_config();
847     pool.max_component_instance_size(1);
848     let mut config = Config::new();
849     config.wasm_component_model(true);
850     config.allocation_strategy(pool);
851     let engine = Engine::new(&config)?;
852 
853     match wasmtime::component::Component::new(&engine, "(component)") {
854         Ok(_) => panic!("should have hit limit"),
855         Err(e) => e.assert_contains(
856             "instance allocation for this component requires 48 bytes of \
857             `VMComponentContext` space which exceeds the configured maximum of 1 bytes",
858         ),
859     }
860 
861     Ok(())
862 }
863 
864 #[test]
865 #[cfg_attr(miri, ignore)]
866 fn total_tables_limit() -> Result<()> {
867     const TOTAL_TABLES: u32 = 5;
868 
869     let mut pool = crate::small_pool_config();
870     pool.total_tables(TOTAL_TABLES)
871         .total_core_instances(TOTAL_TABLES + 1);
872     let mut config = Config::new();
873     config.allocation_strategy(pool);
874 
875     let engine = Engine::new(&config)?;
876     let linker = Linker::new(&engine);
877     let module = Module::new(&engine, "(module (table 0 1 funcref))")?;
878 
879     let mut store = Store::new(&engine, ());
880     for _ in 0..TOTAL_TABLES {
881         linker.instantiate(&mut store, &module)?;
882     }
883 
884     match linker.instantiate(&mut store, &module) {
885         Ok(_) => panic!("should have hit table limit"),
886         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
887     }
888 
889     drop(store);
890     let mut store = Store::new(&engine, ());
891     for _ in 0..TOTAL_TABLES {
892         linker.instantiate(&mut store, &module)?;
893     }
894 
895     Ok(())
896 }
897 
898 #[tokio::test]
899 #[cfg(not(miri))]
900 async fn total_stacks_limit() -> Result<()> {
901     use super::async_functions::PollOnce;
902 
903     const TOTAL_STACKS: u32 = 2;
904 
905     let mut pool = crate::small_pool_config();
906     pool.total_stacks(TOTAL_STACKS)
907         .total_core_instances(TOTAL_STACKS + 1);
908     let mut config = Config::new();
909     config.async_support(true);
910     config.allocation_strategy(pool);
911 
912     let engine = Engine::new(&config)?;
913 
914     let mut linker = Linker::new(&engine);
915     linker.func_new_async(
916         "async",
917         "yield",
918         FuncType::new(&engine, [], []),
919         |_caller, _params, _results| {
920             Box::new(async {
921                 tokio::task::yield_now().await;
922                 Ok(())
923             })
924         },
925     )?;
926 
927     let module = Module::new(
928         &engine,
929         r#"
930         (module
931             (import "async" "yield" (func $yield))
932             (func (export "run")
933                 call $yield
934             )
935         )
936     "#,
937     )?;
938 
939     // Allocate stacks up to the limit. (Poll the futures once to make sure we
940     // actually enter Wasm and force a stack allocation.)
941 
942     let mut store1 = Store::new(&engine, ());
943     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
944     let run1 = instance1.get_func(&mut store1, "run").unwrap();
945     let future1 = PollOnce::new(Box::pin(run1.call_async(store1, &[], &mut [])))
946         .await
947         .unwrap_err();
948 
949     let mut store2 = Store::new(&engine, ());
950     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
951     let run2 = instance2.get_func(&mut store2, "run").unwrap();
952     let future2 = PollOnce::new(Box::pin(run2.call_async(store2, &[], &mut [])))
953         .await
954         .unwrap_err();
955 
956     // Allocating more should fail.
957     let mut store3 = Store::new(&engine, ());
958     match linker.instantiate_async(&mut store3, &module).await {
959         Ok(_) => panic!("should have hit stack limit"),
960         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
961     }
962 
963     // Finish the futures and return their Wasm stacks to the pool.
964     future1.await?;
965     future2.await?;
966 
967     // Should be able to allocate new stacks again.
968     let mut store1 = Store::new(&engine, ());
969     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
970     let run1 = instance1.get_func(&mut store1, "run").unwrap();
971     let future1 = run1.call_async(&mut store1, &[], &mut []);
972 
973     let mut store2 = Store::new(&engine, ());
974     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
975     let run2 = instance2.get_func(&mut store2, "run").unwrap();
976     let future2 = run2.call_async(&mut store2, &[], &mut []);
977 
978     future1.await?;
979     future2.await?;
980 
981     // Dispose one store via `Drop`, the other via `into_data`, and ensure that
982     // any lingering stacks make their way back to the pool.
983     drop(store1);
984     store2.into_data();
985 
986     Ok(())
987 }
988 
989 #[test]
990 #[cfg(feature = "component-model")]
991 fn component_core_instances_limit() -> Result<()> {
992     let mut pool = crate::small_pool_config();
993     pool.max_core_instances_per_component(1);
994     let mut config = Config::new();
995     config.wasm_component_model(true);
996     config.allocation_strategy(pool);
997     let engine = Engine::new(&config)?;
998 
999     // One core instance works.
1000     wasmtime::component::Component::new(
1001         &engine,
1002         r#"
1003             (component
1004                 (core module $m)
1005                 (core instance $a (instantiate $m))
1006             )
1007         "#,
1008     )?;
1009 
1010     // Two core instances doesn't.
1011     match wasmtime::component::Component::new(
1012         &engine,
1013         r#"
1014             (component
1015                 (core module $m)
1016                 (core instance $a (instantiate $m))
1017                 (core instance $b (instantiate $m))
1018             )
1019         "#,
1020     ) {
1021         Ok(_) => panic!("should have hit limit"),
1022         Err(e) => e.assert_contains(
1023             "The component transitively contains 2 core module instances, which exceeds the \
1024              configured maximum of 1",
1025         ),
1026     }
1027 
1028     Ok(())
1029 }
1030 
1031 #[test]
1032 #[cfg(feature = "component-model")]
1033 fn component_memories_limit() -> Result<()> {
1034     let mut pool = crate::small_pool_config();
1035     pool.max_memories_per_component(1).total_memories(2);
1036     let mut config = Config::new();
1037     config.wasm_component_model(true);
1038     config.allocation_strategy(pool);
1039     let engine = Engine::new(&config)?;
1040 
1041     // One memory works.
1042     wasmtime::component::Component::new(
1043         &engine,
1044         r#"
1045             (component
1046                 (core module $m (memory 1 1))
1047                 (core instance $a (instantiate $m))
1048             )
1049         "#,
1050     )?;
1051 
1052     // Two memories doesn't.
1053     match wasmtime::component::Component::new(
1054         &engine,
1055         r#"
1056             (component
1057                 (core module $m (memory 1 1))
1058                 (core instance $a (instantiate $m))
1059                 (core instance $b (instantiate $m))
1060             )
1061         "#,
1062     ) {
1063         Ok(_) => panic!("should have hit limit"),
1064         Err(e) => e.assert_contains(
1065             "The component transitively contains 2 Wasm linear memories, which exceeds the \
1066              configured maximum of 1",
1067         ),
1068     }
1069 
1070     Ok(())
1071 }
1072 
1073 #[test]
1074 #[cfg(feature = "component-model")]
1075 fn component_tables_limit() -> Result<()> {
1076     let mut pool = crate::small_pool_config();
1077     pool.max_tables_per_component(1).total_tables(2);
1078     let mut config = Config::new();
1079     config.wasm_component_model(true);
1080     config.allocation_strategy(pool);
1081     let engine = Engine::new(&config)?;
1082 
1083     // One table works.
1084     wasmtime::component::Component::new(
1085         &engine,
1086         r#"
1087             (component
1088                 (core module $m (table 1 1 funcref))
1089                 (core instance $a (instantiate $m))
1090             )
1091         "#,
1092     )?;
1093 
1094     // Two tables doesn't.
1095     match wasmtime::component::Component::new(
1096         &engine,
1097         r#"
1098             (component
1099                 (core module $m (table 1 1 funcref))
1100                 (core instance $a (instantiate $m))
1101                 (core instance $b (instantiate $m))
1102             )
1103         "#,
1104     ) {
1105         Ok(_) => panic!("should have hit limit"),
1106         Err(e) => e.assert_contains(
1107             "The component transitively contains 2 tables, which exceeds the \
1108              configured maximum of 1",
1109         ),
1110     }
1111 
1112     Ok(())
1113 }
1114 
1115 #[test]
1116 #[cfg_attr(miri, ignore)]
1117 fn total_memories_limit() -> Result<()> {
1118     const TOTAL_MEMORIES: u32 = 5;
1119 
1120     let mut pool = crate::small_pool_config();
1121     pool.total_memories(TOTAL_MEMORIES)
1122         .total_core_instances(TOTAL_MEMORIES + 1)
1123         .memory_protection_keys(MpkEnabled::Disable);
1124     let mut config = Config::new();
1125     config.allocation_strategy(pool);
1126 
1127     let engine = Engine::new(&config)?;
1128     let linker = Linker::new(&engine);
1129     let module = Module::new(&engine, "(module (memory 1 1))")?;
1130 
1131     let mut store = Store::new(&engine, ());
1132     for _ in 0..TOTAL_MEMORIES {
1133         linker.instantiate(&mut store, &module)?;
1134     }
1135 
1136     match linker.instantiate(&mut store, &module) {
1137         Ok(_) => panic!("should have hit memory limit"),
1138         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
1139     }
1140 
1141     drop(store);
1142     let mut store = Store::new(&engine, ());
1143     for _ in 0..TOTAL_MEMORIES {
1144         linker.instantiate(&mut store, &module)?;
1145     }
1146 
1147     Ok(())
1148 }
1149 
1150 #[test]
1151 #[cfg_attr(miri, ignore)]
1152 fn decommit_batching() -> Result<()> {
1153     for (capacity, batch_size) in [
1154         // A reasonable batch size.
1155         (10, 5),
1156         // Batch sizes of zero and one should effectively disable batching.
1157         (10, 1),
1158         (10, 0),
1159         // A bigger batch size than capacity, which forces the allocation path
1160         // to flush the decommit queue.
1161         (10, 99),
1162     ] {
1163         let mut pool = crate::small_pool_config();
1164         pool.total_memories(capacity)
1165             .total_core_instances(capacity)
1166             .decommit_batch_size(batch_size)
1167             .memory_protection_keys(MpkEnabled::Disable);
1168         let mut config = Config::new();
1169         config.allocation_strategy(pool);
1170 
1171         let engine = Engine::new(&config)?;
1172         let linker = Linker::new(&engine);
1173         let module = Module::new(&engine, "(module (memory 1 1))")?;
1174 
1175         // Just make sure that we can instantiate all slots a few times and the
1176         // pooling allocator must be flushing the decommit queue as necessary.
1177         for _ in 0..3 {
1178             let mut store = Store::new(&engine, ());
1179             for _ in 0..capacity {
1180                 linker.instantiate(&mut store, &module)?;
1181             }
1182         }
1183     }
1184 
1185     Ok(())
1186 }
1187 
1188 #[test]
1189 fn tricky_empty_table_with_empty_virtual_memory_alloc() -> Result<()> {
1190     // Configure the pooling allocator to have no access to virtual memory, e.g.
1191     // no table elements but a single table. This should technically support a
1192     // single empty table being allocated into it but virtual memory isn't
1193     // actually allocated here.
1194     let mut cfg = PoolingAllocationConfig::default();
1195     cfg.table_elements(0);
1196     cfg.total_memories(0);
1197     cfg.total_tables(1);
1198     cfg.total_stacks(0);
1199     cfg.total_core_instances(1);
1200     cfg.max_memory_size(0);
1201 
1202     let mut c = Config::new();
1203     c.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1204 
1205     // Disable lazy init to actually try to get this to do something interesting
1206     // at runtime.
1207     c.table_lazy_init(false);
1208 
1209     let engine = Engine::new(&c)?;
1210 
1211     // This module has a single empty table, with a single empty element
1212     // segment. Nothing actually goes wrong here, it should instantiate
1213     // successfully. Along the way though the empty mmap above will get viewed
1214     // as an array-of-pointers, so everything internally should all line up to
1215     // work ok.
1216     let module = Module::new(
1217         &engine,
1218         r#"
1219 (module
1220     (table 0 funcref)
1221     (elem (i32.const 0) func)
1222 )
1223 "#,
1224     )?;
1225     let mut store = Store::new(&engine, ());
1226     Instance::new(&mut store, &module, &[])?;
1227     Ok(())
1228 }
1229 
1230 #[test]
1231 #[cfg_attr(miri, ignore)]
1232 fn shared_memory_unsupported() -> Result<()> {
1233     // Skip this test on platforms that don't support threads.
1234     if crate::threads::engine().is_none() {
1235         return Ok(());
1236     }
1237     let mut config = Config::new();
1238     let mut cfg = PoolingAllocationConfig::default();
1239     // shrink the size of this allocator
1240     cfg.total_memories(1);
1241     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1242     let engine = Engine::new(&config)?;
1243 
1244     let err = Module::new(
1245         &engine,
1246         r#"
1247             (module
1248                 (memory 5 5 shared)
1249             )
1250         "#,
1251     )
1252     .unwrap_err();
1253     err.assert_contains(
1254         "memory index 0 is shared which is not supported \
1255          in the pooling allocator",
1256     );
1257     Ok(())
1258 }
1259 
1260 #[test]
1261 #[cfg_attr(miri, ignore)]
1262 fn custom_page_sizes_reusing_same_slot() -> Result<()> {
1263     let mut config = Config::new();
1264     config.wasm_custom_page_sizes(true);
1265     let mut cfg = crate::small_pool_config();
1266     // force the memories below to collide in the same memory slot
1267     cfg.total_memories(1);
1268     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1269     let engine = Engine::new(&config)?;
1270 
1271     // Instantiate one module, leaving the slot 5 bytes big (but one page
1272     // accessible)
1273     {
1274         let m1 = Module::new(
1275             &engine,
1276             r#"
1277                 (module
1278                     (memory 5 (pagesize 1))
1279 
1280                     (data (i32.const 0) "a")
1281                 )
1282             "#,
1283         )?;
1284         let mut store = Store::new(&engine, ());
1285         Instance::new(&mut store, &m1, &[])?;
1286     }
1287 
1288     // Instantiate a second module, which should work
1289     {
1290         let m2 = Module::new(
1291             &engine,
1292             r#"
1293                 (module
1294                     (memory 6 (pagesize 1))
1295 
1296                     (data (i32.const 0) "a")
1297                 )
1298             "#,
1299         )?;
1300         let mut store = Store::new(&engine, ());
1301         Instance::new(&mut store, &m2, &[])?;
1302     }
1303     Ok(())
1304 }
1305 
1306 #[test]
1307 #[cfg_attr(miri, ignore)]
1308 fn instantiate_non_page_aligned_sizes() -> Result<()> {
1309     let mut config = Config::new();
1310     config.wasm_custom_page_sizes(true);
1311     let mut cfg = crate::small_pool_config();
1312     cfg.total_memories(1);
1313     cfg.max_memory_size(761927);
1314     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1315     let engine = Engine::new(&config)?;
1316 
1317     let module = Module::new(
1318         &engine,
1319         r#"
1320             (module
1321               (memory 761927 761927 (pagesize 0x1))
1322             )
1323         "#,
1324     )?;
1325     let mut store = Store::new(&engine, ());
1326     Instance::new(&mut store, &module, &[])?;
1327     Ok(())
1328 }
1329