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