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