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