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: u32 = 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);
303         assert_eq!(
304             table
305                 .grow(&mut store, 1, Ref::Func(None))
306                 .expect("table should grow"),
307             i
308         );
309     }
310 
311     assert_eq!(table.size(&store), TABLE_ELEMENTS);
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
401                 .set(&mut store, i, Ref::Func(Some(f.clone())))
402                 .unwrap();
403         }
404     }
405 
406     Ok(())
407 }
408 
409 #[test]
410 fn total_core_instances_limit() -> Result<()> {
411     const INSTANCE_LIMIT: u32 = 10;
412     let mut pool = crate::small_pool_config();
413     pool.total_core_instances(INSTANCE_LIMIT);
414     let mut config = Config::new();
415     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
416     config.dynamic_memory_guard_size(0);
417     config.static_memory_guard_size(0);
418     config.static_memory_maximum_size(1 << 16);
419 
420     let engine = Engine::new(&config)?;
421     let module = Module::new(&engine, r#"(module)"#)?;
422 
423     // Instantiate to the limit
424     {
425         let mut store = Store::new(&engine, ());
426 
427         for _ in 0..INSTANCE_LIMIT {
428             Instance::new(&mut store, &module, &[])?;
429         }
430 
431         match Instance::new(&mut store, &module, &[]) {
432             Ok(_) => panic!("instantiation should fail"),
433             Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
434         }
435     }
436 
437     // With the above store dropped, ensure instantiations can be made
438 
439     let mut store = Store::new(&engine, ());
440 
441     for _ in 0..INSTANCE_LIMIT {
442         Instance::new(&mut store, &module, &[])?;
443     }
444 
445     Ok(())
446 }
447 
448 #[test]
449 fn preserve_data_segments() -> Result<()> {
450     let mut pool = crate::small_pool_config();
451     pool.total_memories(2);
452     let mut config = Config::new();
453     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
454     let engine = Engine::new(&config)?;
455     let m = Module::new(
456         &engine,
457         r#"
458             (module
459                 (memory (export "mem") 1 1)
460                 (data (i32.const 0) "foo"))
461         "#,
462     )?;
463     let mut store = Store::new(&engine, ());
464     let i = Instance::new(&mut store, &m, &[])?;
465 
466     // Drop the module. This should *not* drop the actual data referenced by the
467     // module.
468     drop(m);
469 
470     // Spray some stuff on the heap. If wasm data lived on the heap this should
471     // paper over things and help us catch use-after-free here if it would
472     // otherwise happen.
473     if !cfg!(miri) {
474         let mut strings = Vec::new();
475         for _ in 0..1000 {
476             let mut string = String::new();
477             for _ in 0..1000 {
478                 string.push('g');
479             }
480             strings.push(string);
481         }
482         drop(strings);
483     }
484 
485     let mem = i.get_memory(&mut store, "mem").unwrap();
486 
487     // Hopefully it's still `foo`!
488     assert!(mem.data(&store).starts_with(b"foo"));
489 
490     Ok(())
491 }
492 
493 #[test]
494 fn multi_memory_with_imported_memories() -> Result<()> {
495     // This test checks that the base address for the defined memory is correct for the instance
496     // despite the presence of an imported memory.
497 
498     let mut pool = crate::small_pool_config();
499     pool.total_memories(2).max_memories_per_module(2);
500     let mut config = Config::new();
501     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
502     config.wasm_multi_memory(true);
503 
504     let engine = Engine::new(&config)?;
505     let module = Module::new(
506         &engine,
507         r#"(module (import "" "m1" (memory 0)) (memory (export "m2") 1))"#,
508     )?;
509 
510     let mut store = Store::new(&engine, ());
511 
512     let m1 = Memory::new(&mut store, MemoryType::new(0, None))?;
513     let instance = Instance::new(&mut store, &module, &[m1.into()])?;
514 
515     let m2 = instance.get_memory(&mut store, "m2").unwrap();
516 
517     m2.data_mut(&mut store)[0] = 0x42;
518     assert_eq!(m2.data(&store)[0], 0x42);
519 
520     Ok(())
521 }
522 
523 #[test]
524 fn drop_externref_global_during_module_init() -> Result<()> {
525     struct Limiter;
526 
527     impl ResourceLimiter for Limiter {
528         fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> {
529             Ok(false)
530         }
531 
532         fn table_growing(&mut self, _: u32, _: u32, _: Option<u32>) -> Result<bool> {
533             Ok(false)
534         }
535     }
536 
537     let pool = crate::small_pool_config();
538     let mut config = Config::new();
539     config.wasm_reference_types(true);
540     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
541 
542     let engine = Engine::new(&config)?;
543 
544     let module = Module::new(
545         &engine,
546         r#"
547             (module
548                 (global i32 (i32.const 1))
549                 (global i32 (i32.const 2))
550                 (global i32 (i32.const 3))
551                 (global i32 (i32.const 4))
552                 (global i32 (i32.const 5))
553             )
554         "#,
555     )?;
556 
557     let mut store = Store::new(&engine, Limiter);
558     Instance::new(&mut store, &module, &[])?;
559     drop(store);
560 
561     let module = Module::new(
562         &engine,
563         r#"
564             (module
565                 (memory 1)
566                 (global (mut externref) (ref.null extern))
567             )
568         "#,
569     )?;
570 
571     let mut store = Store::new(&engine, Limiter);
572     store.limiter(|s| s);
573     assert!(Instance::new(&mut store, &module, &[]).is_err());
574 
575     Ok(())
576 }
577 
578 #[test]
579 #[cfg_attr(miri, ignore)]
580 fn switch_image_and_non_image() -> Result<()> {
581     let pool = crate::small_pool_config();
582     let mut c = Config::new();
583     c.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
584     let engine = Engine::new(&c)?;
585     let module1 = Module::new(
586         &engine,
587         r#"
588             (module
589                 (memory 1)
590                 (func (export "load") (param i32) (result i32)
591                     local.get 0
592                     i32.load
593                 )
594             )
595         "#,
596     )?;
597     let module2 = Module::new(
598         &engine,
599         r#"
600             (module
601                 (memory (export "memory") 1)
602                 (data (i32.const 0) "1234")
603             )
604         "#,
605     )?;
606 
607     let assert_zero = || -> Result<()> {
608         let mut store = Store::new(&engine, ());
609         let instance = Instance::new(&mut store, &module1, &[])?;
610         let func = instance.get_typed_func::<i32, i32>(&mut store, "load")?;
611         assert_eq!(func.call(&mut store, 0)?, 0);
612         Ok(())
613     };
614 
615     // Initialize with a heap image and make sure the next instance, without an
616     // image, is zeroed
617     Instance::new(&mut Store::new(&engine, ()), &module2, &[])?;
618     assert_zero()?;
619 
620     // ... transition back to heap image and do this again
621     Instance::new(&mut Store::new(&engine, ()), &module2, &[])?;
622     assert_zero()?;
623 
624     // And go back to an image and make sure it's read/write on the host.
625     let mut store = Store::new(&engine, ());
626     let instance = Instance::new(&mut store, &module2, &[])?;
627     let memory = instance.get_memory(&mut store, "memory").unwrap();
628     let mem = memory.data_mut(&mut store);
629     assert!(mem.starts_with(b"1234"));
630     mem[..6].copy_from_slice(b"567890");
631 
632     Ok(())
633 }
634 
635 #[test]
636 #[cfg(target_pointer_width = "64")]
637 #[cfg_attr(miri, ignore)]
638 fn instance_too_large() -> Result<()> {
639     let mut pool = crate::small_pool_config();
640     pool.max_core_instance_size(16);
641     let mut config = Config::new();
642     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
643 
644     let engine = Engine::new(&config)?;
645     let expected = if cfg!(feature = "wmemcheck") {
646         "\
647         instance allocation for this module requires 336 bytes which exceeds the \
648 configured maximum of 16 bytes; breakdown of allocation requirement:
649 
650  * 71.43% - 240 bytes - instance state management
651  * 26.19% - 88 bytes - static vmctx data
652 "
653     } else {
654         "\
655         instance allocation for this module requires 240 bytes which exceeds the \
656 configured maximum of 16 bytes; breakdown of allocation requirement:
657 
658  * 60.00% - 144 bytes - instance state management
659  * 36.67% - 88 bytes - static vmctx data
660 "
661     };
662     match Module::new(&engine, "(module)") {
663         Ok(_) => panic!("should have failed to compile"),
664         Err(e) => assert_eq!(e.to_string(), expected),
665     }
666 
667     let mut lots_of_globals = format!("(module");
668     for _ in 0..100 {
669         lots_of_globals.push_str("(global i32 i32.const 0)\n");
670     }
671     lots_of_globals.push_str(")");
672 
673     let expected = if cfg!(feature = "wmemcheck") {
674         "\
675 instance allocation for this module requires 1936 bytes which exceeds the \
676 configured maximum of 16 bytes; breakdown of allocation requirement:
677 
678  * 12.40% - 240 bytes - instance state management
679  * 82.64% - 1600 bytes - defined globals
680 "
681     } else {
682         "\
683 instance allocation for this module requires 1840 bytes which exceeds the \
684 configured maximum of 16 bytes; breakdown of allocation requirement:
685 
686  * 7.83% - 144 bytes - instance state management
687  * 86.96% - 1600 bytes - defined globals
688 "
689     };
690     match Module::new(&engine, &lots_of_globals) {
691         Ok(_) => panic!("should have failed to compile"),
692         Err(e) => assert_eq!(e.to_string(), expected),
693     }
694 
695     Ok(())
696 }
697 
698 #[test]
699 #[cfg_attr(miri, ignore)]
700 fn dynamic_memory_pooling_allocator() -> Result<()> {
701     for guard_size in [0, 1 << 16] {
702         let max_size = 128 << 20;
703         let mut pool = crate::small_pool_config();
704         pool.max_memory_size(max_size as usize);
705         let mut config = Config::new();
706         config.static_memory_maximum_size(max_size);
707         config.dynamic_memory_guard_size(guard_size);
708         config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
709 
710         let engine = Engine::new(&config)?;
711 
712         let module = Module::new(
713             &engine,
714             r#"
715             (module
716                 (memory (export "memory") 1)
717 
718                 (func (export "grow") (param i32) (result i32)
719                     local.get 0
720                     memory.grow)
721 
722                 (func (export "size") (result i32)
723                     memory.size)
724 
725                 (func (export "i32.load") (param i32) (result i32)
726                     local.get 0
727                     i32.load)
728 
729                 (func (export "i32.store") (param i32 i32)
730                     local.get 0
731                     local.get 1
732                     i32.store)
733 
734                 (data (i32.const 100) "x")
735             )
736          "#,
737         )?;
738 
739         let mut store = Store::new(&engine, ());
740         let instance = Instance::new(&mut store, &module, &[])?;
741 
742         let grow = instance.get_typed_func::<u32, i32>(&mut store, "grow")?;
743         let size = instance.get_typed_func::<(), u32>(&mut store, "size")?;
744         let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
745         let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?;
746         let memory = instance.get_memory(&mut store, "memory").unwrap();
747 
748         // basic length 1 tests
749         // assert_eq!(memory.grow(&mut store, 1)?, 0);
750         assert_eq!(memory.size(&store), 1);
751         assert_eq!(size.call(&mut store, ())?, 1);
752         assert_eq!(i32_load.call(&mut store, 0)?, 0);
753         assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x'));
754         i32_store.call(&mut store, (0, 0))?;
755         i32_store.call(&mut store, (100, i32::from(b'y')))?;
756         assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y'));
757 
758         // basic length 2 tests
759         let page = 64 * 1024;
760         assert_eq!(grow.call(&mut store, 1)?, 1);
761         assert_eq!(memory.size(&store), 2);
762         assert_eq!(size.call(&mut store, ())?, 2);
763         i32_store.call(&mut store, (page, 200))?;
764         assert_eq!(i32_load.call(&mut store, page)?, 200);
765 
766         // test writes are visible
767         i32_store.call(&mut store, (2, 100))?;
768         assert_eq!(i32_load.call(&mut store, 2)?, 100);
769 
770         // test growth can't exceed maximum
771         let too_many = max_size / (64 * 1024);
772         assert_eq!(grow.call(&mut store, too_many as u32)?, -1);
773         assert!(memory.grow(&mut store, too_many).is_err());
774 
775         assert_eq!(memory.data(&store)[page as usize], 200);
776 
777         // Re-instantiate in another store.
778         store = Store::new(&engine, ());
779         let instance = Instance::new(&mut store, &module, &[])?;
780         let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
781         let memory = instance.get_memory(&mut store, "memory").unwrap();
782 
783         // This is out of bounds...
784         assert!(i32_load.call(&mut store, page).is_err());
785         assert_eq!(memory.data_size(&store), page as usize);
786 
787         // ... but implementation-wise it should still be mapped memory from
788         // before if we don't have any guard pages.
789         //
790         // Note though that prior writes should all appear as zeros and we can't see
791         // data from the prior instance.
792         //
793         // Note that this part is only implemented on Linux which has
794         // `MADV_DONTNEED`.
795         if cfg!(target_os = "linux") && guard_size == 0 {
796             unsafe {
797                 let ptr = memory.data_ptr(&store);
798                 assert_eq!(*ptr.offset(page as isize), 0);
799             }
800         }
801     }
802 
803     Ok(())
804 }
805 
806 #[test]
807 #[cfg_attr(miri, ignore)]
808 fn zero_memory_pages_disallows_oob() -> Result<()> {
809     let mut pool = crate::small_pool_config();
810     pool.max_memory_size(0);
811     let mut config = Config::new();
812     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
813 
814     let engine = Engine::new(&config)?;
815     let module = Module::new(
816         &engine,
817         r#"
818             (module
819                 (memory 0)
820 
821                 (func (export "load") (param i32) (result i32)
822                     local.get 0
823                     i32.load)
824 
825                 (func (export "store") (param i32 )
826                     local.get 0
827                     local.get 0
828                     i32.store)
829             )
830         "#,
831     )?;
832     let mut store = Store::new(&engine, ());
833     let instance = Instance::new(&mut store, &module, &[])?;
834     let load32 = instance.get_typed_func::<i32, i32>(&mut store, "load")?;
835     let store32 = instance.get_typed_func::<i32, ()>(&mut store, "store")?;
836     for i in 0..31 {
837         assert!(load32.call(&mut store, 1 << i).is_err());
838         assert!(store32.call(&mut store, 1 << i).is_err());
839     }
840     Ok(())
841 }
842 
843 #[test]
844 #[cfg(feature = "component-model")]
845 fn total_component_instances_limit() -> Result<()> {
846     const TOTAL_COMPONENT_INSTANCES: u32 = 5;
847 
848     let mut pool = crate::small_pool_config();
849     pool.total_component_instances(TOTAL_COMPONENT_INSTANCES);
850     let mut config = Config::new();
851     config.wasm_component_model(true);
852     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
853 
854     let engine = Engine::new(&config)?;
855     let linker = wasmtime::component::Linker::new(&engine);
856     let component = wasmtime::component::Component::new(&engine, "(component)")?;
857 
858     let mut store = Store::new(&engine, ());
859     for _ in 0..TOTAL_COMPONENT_INSTANCES {
860         linker.instantiate(&mut store, &component)?;
861     }
862 
863     match linker.instantiate(&mut store, &component) {
864         Ok(_) => panic!("should have hit component instance limit"),
865         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
866     }
867 
868     drop(store);
869     let mut store = Store::new(&engine, ());
870     for _ in 0..TOTAL_COMPONENT_INSTANCES {
871         linker.instantiate(&mut store, &component)?;
872     }
873 
874     Ok(())
875 }
876 
877 #[test]
878 #[cfg(feature = "component-model")]
879 fn component_instance_size_limit() -> Result<()> {
880     let mut pool = crate::small_pool_config();
881     pool.max_component_instance_size(1);
882     let mut config = Config::new();
883     config.wasm_component_model(true);
884     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
885     let engine = Engine::new(&config)?;
886 
887     match wasmtime::component::Component::new(&engine, "(component)") {
888         Ok(_) => panic!("should have hit limit"),
889         Err(e) => assert_eq!(
890             e.to_string(),
891             "instance allocation for this component requires 64 bytes of `VMComponentContext` space \
892              which exceeds the configured maximum of 1 bytes"
893         ),
894     }
895 
896     Ok(())
897 }
898 
899 #[test]
900 #[cfg_attr(miri, ignore)]
901 fn total_tables_limit() -> Result<()> {
902     const TOTAL_TABLES: u32 = 5;
903 
904     let mut pool = crate::small_pool_config();
905     pool.total_tables(TOTAL_TABLES)
906         .total_core_instances(TOTAL_TABLES + 1);
907     let mut config = Config::new();
908     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
909 
910     let engine = Engine::new(&config)?;
911     let linker = Linker::new(&engine);
912     let module = Module::new(&engine, "(module (table 0 1 funcref))")?;
913 
914     let mut store = Store::new(&engine, ());
915     for _ in 0..TOTAL_TABLES {
916         linker.instantiate(&mut store, &module)?;
917     }
918 
919     match linker.instantiate(&mut store, &module) {
920         Ok(_) => panic!("should have hit table limit"),
921         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
922     }
923 
924     drop(store);
925     let mut store = Store::new(&engine, ());
926     for _ in 0..TOTAL_TABLES {
927         linker.instantiate(&mut store, &module)?;
928     }
929 
930     Ok(())
931 }
932 
933 #[tokio::test]
934 #[cfg(not(miri))]
935 async fn total_stacks_limit() -> Result<()> {
936     use super::async_functions::PollOnce;
937 
938     const TOTAL_STACKS: u32 = 2;
939 
940     let mut pool = crate::small_pool_config();
941     pool.total_stacks(TOTAL_STACKS)
942         .total_core_instances(TOTAL_STACKS + 1);
943     let mut config = Config::new();
944     config.async_support(true);
945     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
946 
947     let engine = Engine::new(&config)?;
948 
949     let mut linker = Linker::new(&engine);
950     linker.func_new_async(
951         "async",
952         "yield",
953         FuncType::new(&engine, [], []),
954         |_caller, _params, _results| {
955             Box::new(async {
956                 tokio::task::yield_now().await;
957                 Ok(())
958             })
959         },
960     )?;
961 
962     let module = Module::new(
963         &engine,
964         r#"
965         (module
966             (import "async" "yield" (func $yield))
967             (func (export "run")
968                 call $yield
969             )
970         )
971     "#,
972     )?;
973 
974     // Allocate stacks up to the limit. (Poll the futures once to make sure we
975     // actually enter Wasm and force a stack allocation.)
976 
977     let mut store1 = Store::new(&engine, ());
978     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
979     let run1 = instance1.get_func(&mut store1, "run").unwrap();
980     let future1 = PollOnce::new(Box::pin(run1.call_async(store1, &[], &mut [])))
981         .await
982         .unwrap_err();
983 
984     let mut store2 = Store::new(&engine, ());
985     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
986     let run2 = instance2.get_func(&mut store2, "run").unwrap();
987     let future2 = PollOnce::new(Box::pin(run2.call_async(store2, &[], &mut [])))
988         .await
989         .unwrap_err();
990 
991     // Allocating more should fail.
992     let mut store3 = Store::new(&engine, ());
993     match linker.instantiate_async(&mut store3, &module).await {
994         Ok(_) => panic!("should have hit stack limit"),
995         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
996     }
997 
998     // Finish the futures and return their Wasm stacks to the pool.
999     future1.await?;
1000     future2.await?;
1001 
1002     // Should be able to allocate new stacks again.
1003     let mut store1 = Store::new(&engine, ());
1004     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
1005     let run1 = instance1.get_func(&mut store1, "run").unwrap();
1006     let future1 = run1.call_async(store1, &[], &mut []);
1007 
1008     let mut store2 = Store::new(&engine, ());
1009     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
1010     let run2 = instance2.get_func(&mut store2, "run").unwrap();
1011     let future2 = run2.call_async(store2, &[], &mut []);
1012 
1013     future1.await?;
1014     future2.await?;
1015 
1016     Ok(())
1017 }
1018 
1019 #[test]
1020 #[cfg(feature = "component-model")]
1021 fn component_core_instances_limit() -> Result<()> {
1022     let mut pool = crate::small_pool_config();
1023     pool.max_core_instances_per_component(1);
1024     let mut config = Config::new();
1025     config.wasm_component_model(true);
1026     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1027     let engine = Engine::new(&config)?;
1028 
1029     // One core instance works.
1030     wasmtime::component::Component::new(
1031         &engine,
1032         r#"
1033             (component
1034                 (core module $m)
1035                 (core instance $a (instantiate $m))
1036             )
1037         "#,
1038     )?;
1039 
1040     // Two core instances doesn't.
1041     match wasmtime::component::Component::new(
1042         &engine,
1043         r#"
1044             (component
1045                 (core module $m)
1046                 (core instance $a (instantiate $m))
1047                 (core instance $b (instantiate $m))
1048             )
1049         "#,
1050     ) {
1051         Ok(_) => panic!("should have hit limit"),
1052         Err(e) => assert_eq!(
1053             e.to_string(),
1054             "The component transitively contains 2 core module instances, which exceeds the \
1055              configured maximum of 1"
1056         ),
1057     }
1058 
1059     Ok(())
1060 }
1061 
1062 #[test]
1063 #[cfg(feature = "component-model")]
1064 fn component_memories_limit() -> Result<()> {
1065     let mut pool = crate::small_pool_config();
1066     pool.max_memories_per_component(1).total_memories(2);
1067     let mut config = Config::new();
1068     config.wasm_component_model(true);
1069     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1070     let engine = Engine::new(&config)?;
1071 
1072     // One memory works.
1073     wasmtime::component::Component::new(
1074         &engine,
1075         r#"
1076             (component
1077                 (core module $m (memory 1 1))
1078                 (core instance $a (instantiate $m))
1079             )
1080         "#,
1081     )?;
1082 
1083     // Two memories doesn't.
1084     match wasmtime::component::Component::new(
1085         &engine,
1086         r#"
1087             (component
1088                 (core module $m (memory 1 1))
1089                 (core instance $a (instantiate $m))
1090                 (core instance $b (instantiate $m))
1091             )
1092         "#,
1093     ) {
1094         Ok(_) => panic!("should have hit limit"),
1095         Err(e) => assert_eq!(
1096             e.to_string(),
1097             "The component transitively contains 2 Wasm linear memories, which exceeds the \
1098              configured maximum of 1"
1099         ),
1100     }
1101 
1102     Ok(())
1103 }
1104 
1105 #[test]
1106 #[cfg(feature = "component-model")]
1107 fn component_tables_limit() -> Result<()> {
1108     let mut pool = crate::small_pool_config();
1109     pool.max_tables_per_component(1).total_tables(2);
1110     let mut config = Config::new();
1111     config.wasm_component_model(true);
1112     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1113     let engine = Engine::new(&config)?;
1114 
1115     // One table works.
1116     wasmtime::component::Component::new(
1117         &engine,
1118         r#"
1119             (component
1120                 (core module $m (table 1 1 funcref))
1121                 (core instance $a (instantiate $m))
1122             )
1123         "#,
1124     )?;
1125 
1126     // Two tables doesn't.
1127     match wasmtime::component::Component::new(
1128         &engine,
1129         r#"
1130             (component
1131                 (core module $m (table 1 1 funcref))
1132                 (core instance $a (instantiate $m))
1133                 (core instance $b (instantiate $m))
1134             )
1135         "#,
1136     ) {
1137         Ok(_) => panic!("should have hit limit"),
1138         Err(e) => assert_eq!(
1139             e.to_string(),
1140             "The component transitively contains 2 tables, which exceeds the \
1141              configured maximum of 1"
1142         ),
1143     }
1144 
1145     Ok(())
1146 }
1147 
1148 #[test]
1149 #[cfg_attr(miri, ignore)]
1150 fn total_memories_limit() -> Result<()> {
1151     const TOTAL_MEMORIES: u32 = 5;
1152 
1153     let mut pool = crate::small_pool_config();
1154     pool.total_memories(TOTAL_MEMORIES)
1155         .total_core_instances(TOTAL_MEMORIES + 1)
1156         .memory_protection_keys(MpkEnabled::Disable);
1157     let mut config = Config::new();
1158     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1159 
1160     let engine = Engine::new(&config)?;
1161     let linker = Linker::new(&engine);
1162     let module = Module::new(&engine, "(module (memory 1 1))")?;
1163 
1164     let mut store = Store::new(&engine, ());
1165     for _ in 0..TOTAL_MEMORIES {
1166         linker.instantiate(&mut store, &module)?;
1167     }
1168 
1169     match linker.instantiate(&mut store, &module) {
1170         Ok(_) => panic!("should have hit memory limit"),
1171         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
1172     }
1173 
1174     drop(store);
1175     let mut store = Store::new(&engine, ());
1176     for _ in 0..TOTAL_MEMORIES {
1177         linker.instantiate(&mut store, &module)?;
1178     }
1179 
1180     Ok(())
1181 }
1182 
1183 #[test]
1184 #[cfg_attr(miri, ignore)]
1185 fn decommit_batching() -> Result<()> {
1186     for (capacity, batch_size) in [
1187         // A reasonable batch size.
1188         (10, 5),
1189         // Batch sizes of zero and one should effectively disable batching.
1190         (10, 1),
1191         (10, 0),
1192         // A bigger batch size than capacity, which forces the allocation path
1193         // to flush the decommit queue.
1194         (10, 99),
1195     ] {
1196         let mut pool = crate::small_pool_config();
1197         pool.total_memories(capacity)
1198             .total_core_instances(capacity)
1199             .decommit_batch_size(batch_size)
1200             .memory_protection_keys(MpkEnabled::Disable);
1201         let mut config = Config::new();
1202         config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1203 
1204         let engine = Engine::new(&config)?;
1205         let linker = Linker::new(&engine);
1206         let module = Module::new(&engine, "(module (memory 1 1))")?;
1207 
1208         // Just make sure that we can instantiate all slots a few times and the
1209         // pooling allocator must be flushing the decommit queue as necessary.
1210         for _ in 0..3 {
1211             let mut store = Store::new(&engine, ());
1212             for _ in 0..capacity {
1213                 linker.instantiate(&mut store, &module)?;
1214             }
1215         }
1216     }
1217 
1218     Ok(())
1219 }
1220