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