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