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  * 76.19% - 256 bytes - instance state management
643  * 21.43% - 72 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  * 66.67% - 160 bytes - instance state management
651  * 30.00% - 72 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  * 13.22% - 256 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  * 8.70% - 160 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 #[cfg(target_pointer_width = "64")] // error message tailored for 64-bit
872 fn component_instance_size_limit() -> Result<()> {
873     let mut pool = crate::small_pool_config();
874     pool.max_component_instance_size(1);
875     let mut config = Config::new();
876     config.wasm_component_model(true);
877     config.allocation_strategy(pool);
878     let engine = Engine::new(&config)?;
879 
880     match wasmtime::component::Component::new(&engine, "(component)") {
881         Ok(_) => panic!("should have hit limit"),
882         Err(e) => assert_eq!(
883             e.to_string(),
884             "instance allocation for this component requires 48 bytes of `VMComponentContext` space \
885              which exceeds the configured maximum of 1 bytes"
886         ),
887     }
888 
889     Ok(())
890 }
891 
892 #[test]
893 #[cfg_attr(miri, ignore)]
894 fn total_tables_limit() -> Result<()> {
895     const TOTAL_TABLES: u32 = 5;
896 
897     let mut pool = crate::small_pool_config();
898     pool.total_tables(TOTAL_TABLES)
899         .total_core_instances(TOTAL_TABLES + 1);
900     let mut config = Config::new();
901     config.allocation_strategy(pool);
902 
903     let engine = Engine::new(&config)?;
904     let linker = Linker::new(&engine);
905     let module = Module::new(&engine, "(module (table 0 1 funcref))")?;
906 
907     let mut store = Store::new(&engine, ());
908     for _ in 0..TOTAL_TABLES {
909         linker.instantiate(&mut store, &module)?;
910     }
911 
912     match linker.instantiate(&mut store, &module) {
913         Ok(_) => panic!("should have hit table limit"),
914         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
915     }
916 
917     drop(store);
918     let mut store = Store::new(&engine, ());
919     for _ in 0..TOTAL_TABLES {
920         linker.instantiate(&mut store, &module)?;
921     }
922 
923     Ok(())
924 }
925 
926 #[tokio::test]
927 #[cfg(not(miri))]
928 async fn total_stacks_limit() -> Result<()> {
929     use super::async_functions::PollOnce;
930 
931     const TOTAL_STACKS: u32 = 2;
932 
933     let mut pool = crate::small_pool_config();
934     pool.total_stacks(TOTAL_STACKS)
935         .total_core_instances(TOTAL_STACKS + 1);
936     let mut config = Config::new();
937     config.async_support(true);
938     config.allocation_strategy(pool);
939 
940     let engine = Engine::new(&config)?;
941 
942     let mut linker = Linker::new(&engine);
943     linker.func_new_async(
944         "async",
945         "yield",
946         FuncType::new(&engine, [], []),
947         |_caller, _params, _results| {
948             Box::new(async {
949                 tokio::task::yield_now().await;
950                 Ok(())
951             })
952         },
953     )?;
954 
955     let module = Module::new(
956         &engine,
957         r#"
958         (module
959             (import "async" "yield" (func $yield))
960             (func (export "run")
961                 call $yield
962             )
963         )
964     "#,
965     )?;
966 
967     // Allocate stacks up to the limit. (Poll the futures once to make sure we
968     // actually enter Wasm and force a stack allocation.)
969 
970     let mut store1 = Store::new(&engine, ());
971     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
972     let run1 = instance1.get_func(&mut store1, "run").unwrap();
973     let future1 = PollOnce::new(Box::pin(run1.call_async(store1, &[], &mut [])))
974         .await
975         .unwrap_err();
976 
977     let mut store2 = Store::new(&engine, ());
978     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
979     let run2 = instance2.get_func(&mut store2, "run").unwrap();
980     let future2 = PollOnce::new(Box::pin(run2.call_async(store2, &[], &mut [])))
981         .await
982         .unwrap_err();
983 
984     // Allocating more should fail.
985     let mut store3 = Store::new(&engine, ());
986     match linker.instantiate_async(&mut store3, &module).await {
987         Ok(_) => panic!("should have hit stack limit"),
988         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
989     }
990 
991     // Finish the futures and return their Wasm stacks to the pool.
992     future1.await?;
993     future2.await?;
994 
995     // Should be able to allocate new stacks again.
996     let mut store1 = Store::new(&engine, ());
997     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
998     let run1 = instance1.get_func(&mut store1, "run").unwrap();
999     let future1 = run1.call_async(&mut store1, &[], &mut []);
1000 
1001     let mut store2 = Store::new(&engine, ());
1002     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
1003     let run2 = instance2.get_func(&mut store2, "run").unwrap();
1004     let future2 = run2.call_async(&mut store2, &[], &mut []);
1005 
1006     future1.await?;
1007     future2.await?;
1008 
1009     // Dispose one store via `Drop`, the other via `into_data`, and ensure that
1010     // any lingering stacks make their way back to the pool.
1011     drop(store1);
1012     store2.into_data();
1013 
1014     Ok(())
1015 }
1016 
1017 #[test]
1018 #[cfg(feature = "component-model")]
1019 fn component_core_instances_limit() -> Result<()> {
1020     let mut pool = crate::small_pool_config();
1021     pool.max_core_instances_per_component(1);
1022     let mut config = Config::new();
1023     config.wasm_component_model(true);
1024     config.allocation_strategy(pool);
1025     let engine = Engine::new(&config)?;
1026 
1027     // One core instance works.
1028     wasmtime::component::Component::new(
1029         &engine,
1030         r#"
1031             (component
1032                 (core module $m)
1033                 (core instance $a (instantiate $m))
1034             )
1035         "#,
1036     )?;
1037 
1038     // Two core instances doesn't.
1039     match wasmtime::component::Component::new(
1040         &engine,
1041         r#"
1042             (component
1043                 (core module $m)
1044                 (core instance $a (instantiate $m))
1045                 (core instance $b (instantiate $m))
1046             )
1047         "#,
1048     ) {
1049         Ok(_) => panic!("should have hit limit"),
1050         Err(e) => assert_eq!(
1051             e.to_string(),
1052             "The component transitively contains 2 core module instances, which exceeds the \
1053              configured maximum of 1"
1054         ),
1055     }
1056 
1057     Ok(())
1058 }
1059 
1060 #[test]
1061 #[cfg(feature = "component-model")]
1062 fn component_memories_limit() -> Result<()> {
1063     let mut pool = crate::small_pool_config();
1064     pool.max_memories_per_component(1).total_memories(2);
1065     let mut config = Config::new();
1066     config.wasm_component_model(true);
1067     config.allocation_strategy(pool);
1068     let engine = Engine::new(&config)?;
1069 
1070     // One memory works.
1071     wasmtime::component::Component::new(
1072         &engine,
1073         r#"
1074             (component
1075                 (core module $m (memory 1 1))
1076                 (core instance $a (instantiate $m))
1077             )
1078         "#,
1079     )?;
1080 
1081     // Two memories doesn't.
1082     match wasmtime::component::Component::new(
1083         &engine,
1084         r#"
1085             (component
1086                 (core module $m (memory 1 1))
1087                 (core instance $a (instantiate $m))
1088                 (core instance $b (instantiate $m))
1089             )
1090         "#,
1091     ) {
1092         Ok(_) => panic!("should have hit limit"),
1093         Err(e) => assert_eq!(
1094             e.to_string(),
1095             "The component transitively contains 2 Wasm linear memories, which exceeds the \
1096              configured maximum of 1"
1097         ),
1098     }
1099 
1100     Ok(())
1101 }
1102 
1103 #[test]
1104 #[cfg(feature = "component-model")]
1105 fn component_tables_limit() -> Result<()> {
1106     let mut pool = crate::small_pool_config();
1107     pool.max_tables_per_component(1).total_tables(2);
1108     let mut config = Config::new();
1109     config.wasm_component_model(true);
1110     config.allocation_strategy(pool);
1111     let engine = Engine::new(&config)?;
1112 
1113     // One table works.
1114     wasmtime::component::Component::new(
1115         &engine,
1116         r#"
1117             (component
1118                 (core module $m (table 1 1 funcref))
1119                 (core instance $a (instantiate $m))
1120             )
1121         "#,
1122     )?;
1123 
1124     // Two tables doesn't.
1125     match wasmtime::component::Component::new(
1126         &engine,
1127         r#"
1128             (component
1129                 (core module $m (table 1 1 funcref))
1130                 (core instance $a (instantiate $m))
1131                 (core instance $b (instantiate $m))
1132             )
1133         "#,
1134     ) {
1135         Ok(_) => panic!("should have hit limit"),
1136         Err(e) => assert_eq!(
1137             e.to_string(),
1138             "The component transitively contains 2 tables, which exceeds the \
1139              configured maximum of 1"
1140         ),
1141     }
1142 
1143     Ok(())
1144 }
1145 
1146 #[test]
1147 #[cfg_attr(miri, ignore)]
1148 fn total_memories_limit() -> Result<()> {
1149     const TOTAL_MEMORIES: u32 = 5;
1150 
1151     let mut pool = crate::small_pool_config();
1152     pool.total_memories(TOTAL_MEMORIES)
1153         .total_core_instances(TOTAL_MEMORIES + 1)
1154         .memory_protection_keys(MpkEnabled::Disable);
1155     let mut config = Config::new();
1156     config.allocation_strategy(pool);
1157 
1158     let engine = Engine::new(&config)?;
1159     let linker = Linker::new(&engine);
1160     let module = Module::new(&engine, "(module (memory 1 1))")?;
1161 
1162     let mut store = Store::new(&engine, ());
1163     for _ in 0..TOTAL_MEMORIES {
1164         linker.instantiate(&mut store, &module)?;
1165     }
1166 
1167     match linker.instantiate(&mut store, &module) {
1168         Ok(_) => panic!("should have hit memory limit"),
1169         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
1170     }
1171 
1172     drop(store);
1173     let mut store = Store::new(&engine, ());
1174     for _ in 0..TOTAL_MEMORIES {
1175         linker.instantiate(&mut store, &module)?;
1176     }
1177 
1178     Ok(())
1179 }
1180 
1181 #[test]
1182 #[cfg_attr(miri, ignore)]
1183 fn decommit_batching() -> Result<()> {
1184     for (capacity, batch_size) in [
1185         // A reasonable batch size.
1186         (10, 5),
1187         // Batch sizes of zero and one should effectively disable batching.
1188         (10, 1),
1189         (10, 0),
1190         // A bigger batch size than capacity, which forces the allocation path
1191         // to flush the decommit queue.
1192         (10, 99),
1193     ] {
1194         let mut pool = crate::small_pool_config();
1195         pool.total_memories(capacity)
1196             .total_core_instances(capacity)
1197             .decommit_batch_size(batch_size)
1198             .memory_protection_keys(MpkEnabled::Disable);
1199         let mut config = Config::new();
1200         config.allocation_strategy(pool);
1201 
1202         let engine = Engine::new(&config)?;
1203         let linker = Linker::new(&engine);
1204         let module = Module::new(&engine, "(module (memory 1 1))")?;
1205 
1206         // Just make sure that we can instantiate all slots a few times and the
1207         // pooling allocator must be flushing the decommit queue as necessary.
1208         for _ in 0..3 {
1209             let mut store = Store::new(&engine, ());
1210             for _ in 0..capacity {
1211                 linker.instantiate(&mut store, &module)?;
1212             }
1213         }
1214     }
1215 
1216     Ok(())
1217 }
1218 
1219 #[test]
1220 fn tricky_empty_table_with_empty_virtual_memory_alloc() -> Result<()> {
1221     // Configure the pooling allocator to have no access to virtual memory, e.g.
1222     // no table elements but a single table. This should technically support a
1223     // single empty table being allocated into it but virtual memory isn't
1224     // actually allocated here.
1225     let mut cfg = PoolingAllocationConfig::default();
1226     cfg.table_elements(0);
1227     cfg.total_memories(0);
1228     cfg.total_tables(1);
1229     cfg.total_stacks(0);
1230     cfg.total_core_instances(1);
1231     cfg.max_memory_size(0);
1232 
1233     let mut c = Config::new();
1234     c.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1235 
1236     // Disable lazy init to actually try to get this to do something interesting
1237     // at runtime.
1238     c.table_lazy_init(false);
1239 
1240     let engine = Engine::new(&c)?;
1241 
1242     // This module has a single empty table, with a single empty element
1243     // segment. Nothing actually goes wrong here, it should instantiate
1244     // successfully. Along the way though the empty mmap above will get viewed
1245     // as an array-of-pointers, so everything internally should all line up to
1246     // work ok.
1247     let module = Module::new(
1248         &engine,
1249         r#"
1250 (module
1251     (table 0 funcref)
1252     (elem (i32.const 0) func)
1253 )
1254 "#,
1255     )?;
1256     let mut store = Store::new(&engine, ());
1257     Instance::new(&mut store, &module, &[])?;
1258     Ok(())
1259 }
1260 
1261 #[test]
1262 #[cfg_attr(miri, ignore)]
1263 fn shared_memory_unsupported() -> Result<()> {
1264     // Skip this test on platforms that don't support threads.
1265     if crate::threads::engine().is_none() {
1266         return Ok(());
1267     }
1268     let mut config = Config::new();
1269     let mut cfg = PoolingAllocationConfig::default();
1270     // shrink the size of this allocator
1271     cfg.total_memories(1);
1272     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1273     let engine = Engine::new(&config)?;
1274 
1275     let err = Module::new(
1276         &engine,
1277         r#"
1278             (module
1279                 (memory 5 5 shared)
1280             )
1281         "#,
1282     )
1283     .unwrap_err();
1284     let err = err.to_string();
1285     assert!(
1286         err.contains(
1287             "memory index 0 is shared which is not supported \
1288              in the pooling allocator"
1289         ),
1290         "bad error: {err}"
1291     );
1292     Ok(())
1293 }
1294 
1295 #[test]
1296 #[cfg_attr(miri, ignore)]
1297 fn custom_page_sizes_reusing_same_slot() -> Result<()> {
1298     let mut config = Config::new();
1299     config.wasm_custom_page_sizes(true);
1300     let mut cfg = crate::small_pool_config();
1301     // force the memories below to collide in the same memory slot
1302     cfg.total_memories(1);
1303     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1304     let engine = Engine::new(&config)?;
1305 
1306     // Instantiate one module, leaving the slot 5 bytes big (but one page
1307     // accessible)
1308     {
1309         let m1 = Module::new(
1310             &engine,
1311             r#"
1312                 (module
1313                     (memory 5 (pagesize 1))
1314 
1315                     (data (i32.const 0) "a")
1316                 )
1317             "#,
1318         )?;
1319         let mut store = Store::new(&engine, ());
1320         Instance::new(&mut store, &m1, &[])?;
1321     }
1322 
1323     // Instantiate a second module, which should work
1324     {
1325         let m2 = Module::new(
1326             &engine,
1327             r#"
1328                 (module
1329                     (memory 6 (pagesize 1))
1330 
1331                     (data (i32.const 0) "a")
1332                 )
1333             "#,
1334         )?;
1335         let mut store = Store::new(&engine, ());
1336         Instance::new(&mut store, &m2, &[])?;
1337     }
1338     Ok(())
1339 }
1340 
1341 #[test]
1342 #[cfg_attr(miri, ignore)]
1343 fn instantiate_non_page_aligned_sizes() -> Result<()> {
1344     let mut config = Config::new();
1345     config.wasm_custom_page_sizes(true);
1346     let mut cfg = crate::small_pool_config();
1347     cfg.total_memories(1);
1348     cfg.max_memory_size(761927);
1349     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1350     let engine = Engine::new(&config)?;
1351 
1352     let module = Module::new(
1353         &engine,
1354         r#"
1355             (module
1356               (memory 761927 761927 (pagesize 0x1))
1357             )
1358         "#,
1359     )?;
1360     let mut store = Store::new(&engine, ());
1361     Instance::new(&mut store, &module, &[])?;
1362     Ok(())
1363 }
1364