1 use super::{ErrorExt, skip_pooling_allocator_tests};
2 use wasmtime::*;
3 
4 #[test]
successful_instantiation() -> Result<()>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)]
memory_limit() -> Result<()>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]
memory_init() -> Result<()>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)]
memory_guard_page_trap() -> Result<()>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]
memory_zeroed() -> Result<()>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)]
table_limit() -> Result<()>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)]
table_init() -> Result<()>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]
table_zeroed() -> Result<()>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]
total_core_instances_limit() -> Result<()>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]
preserve_data_segments() -> Result<()>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]
multi_memory_with_imported_memories() -> Result<()>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]
drop_externref_global_during_module_init() -> Result<()>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)]
switch_image_and_non_image() -> Result<()>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)]
instance_too_large() -> Result<()>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)]
dynamic_memory_pooling_allocator() -> Result<()>671 fn dynamic_memory_pooling_allocator() -> Result<()> {
672     for guard_size in [0, 1 << 16] {
673         for signals_based_traps in [false, true] {
674             let max_size = 128 << 20;
675             let mut pool = crate::small_pool_config();
676             pool.max_memory_size(max_size as usize);
677             let mut config = Config::new();
678             config.memory_reservation(max_size);
679             config.memory_guard_size(guard_size);
680             config.allocation_strategy(pool);
681             config.signals_based_traps(signals_based_traps);
682 
683             let engine = Engine::new(&config)?;
684 
685             let Ok(module) = Module::new(
686                 &engine,
687                 r#"
688                     (module
689                         (memory (export "memory") 1)
690 
691                         (func (export "grow") (param i32) (result i32)
692                             local.get 0
693                             memory.grow)
694 
695                         (func (export "size") (result i32)
696                             memory.size)
697 
698                         (func (export "i32.load") (param i32) (result i32)
699                             local.get 0
700                             i32.load)
701 
702                         (func (export "i32.store") (param i32 i32)
703                             local.get 0
704                             local.get 1
705                             i32.store)
706 
707                         (data (i32.const 100) "x")
708                     )
709                  "#,
710             ) else {
711                 // Ignore invalid configurations on 32-bit which can't run with
712                 // signals-based-traps.
713                 assert!(cfg!(target_pointer_width = "32") && signals_based_traps);
714                 continue;
715             };
716 
717             let mut store = Store::new(&engine, ());
718             let instance = Instance::new(&mut store, &module, &[])?;
719 
720             let grow = instance.get_typed_func::<u32, i32>(&mut store, "grow")?;
721             let size = instance.get_typed_func::<(), u32>(&mut store, "size")?;
722             let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
723             let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?;
724             let memory = instance.get_memory(&mut store, "memory").unwrap();
725 
726             // basic length 1 tests
727             // assert_eq!(memory.grow(&mut store, 1)?, 0);
728             assert_eq!(memory.size(&store), 1);
729             assert_eq!(size.call(&mut store, ())?, 1);
730             assert_eq!(i32_load.call(&mut store, 0)?, 0);
731             assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x'));
732             i32_store.call(&mut store, (0, 0))?;
733             i32_store.call(&mut store, (100, i32::from(b'y')))?;
734             assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y'));
735 
736             // basic length 2 tests
737             let page = 64 * 1024;
738             assert_eq!(grow.call(&mut store, 1)?, 1);
739             assert_eq!(memory.size(&store), 2);
740             assert_eq!(size.call(&mut store, ())?, 2);
741             i32_store.call(&mut store, (page, 200))?;
742             assert_eq!(i32_load.call(&mut store, page)?, 200);
743 
744             // test writes are visible
745             i32_store.call(&mut store, (2, 100))?;
746             assert_eq!(i32_load.call(&mut store, 2)?, 100);
747 
748             // test growth can't exceed maximum
749             let too_many = max_size / (64 * 1024);
750             assert_eq!(grow.call(&mut store, too_many as u32)?, -1);
751             assert!(memory.grow(&mut store, too_many).is_err());
752 
753             assert_eq!(memory.data(&store)[page as usize], 200);
754 
755             // Re-instantiate in another store.
756             store = Store::new(&engine, ());
757             let instance = Instance::new(&mut store, &module, &[])?;
758             let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?;
759             let memory = instance.get_memory(&mut store, "memory").unwrap();
760 
761             // This is out of bounds...
762             assert!(i32_load.call(&mut store, page).is_err());
763             assert_eq!(memory.data_size(&store), page as usize);
764 
765             // ... but implementation-wise it should still be mapped memory from
766             // before if we don't have any guard pages.
767             //
768             // Note though that prior writes should all appear as zeros and we can't see
769             // data from the prior instance.
770             //
771             // Note that this part is only implemented on Linux which has
772             // `MADV_DONTNEED`.
773             if cfg!(target_os = "linux") && guard_size == 0 && !signals_based_traps {
774                 unsafe {
775                     let ptr = memory.data_ptr(&store);
776                     assert_eq!(*ptr.offset(page as isize), 0);
777                 }
778             }
779         }
780     }
781 
782     Ok(())
783 }
784 
785 #[test]
786 #[cfg_attr(miri, ignore)]
zero_memory_pages_disallows_oob() -> Result<()>787 fn zero_memory_pages_disallows_oob() -> Result<()> {
788     let mut pool = crate::small_pool_config();
789     pool.max_memory_size(0);
790     let mut config = Config::new();
791     config.allocation_strategy(pool);
792 
793     let engine = Engine::new(&config)?;
794     let module = Module::new(
795         &engine,
796         r#"
797             (module
798                 (memory 0)
799 
800                 (func (export "load") (param i32) (result i32)
801                     local.get 0
802                     i32.load)
803 
804                 (func (export "store") (param i32 )
805                     local.get 0
806                     local.get 0
807                     i32.store)
808             )
809         "#,
810     )?;
811     let mut store = Store::new(&engine, ());
812     let instance = Instance::new(&mut store, &module, &[])?;
813     let load32 = instance.get_typed_func::<i32, i32>(&mut store, "load")?;
814     let store32 = instance.get_typed_func::<i32, ()>(&mut store, "store")?;
815     for i in 0..31 {
816         assert!(load32.call(&mut store, 1 << i).is_err());
817         assert!(store32.call(&mut store, 1 << i).is_err());
818     }
819     Ok(())
820 }
821 
822 #[test]
823 #[cfg(feature = "component-model")]
total_component_instances_limit() -> Result<()>824 fn total_component_instances_limit() -> Result<()> {
825     const TOTAL_COMPONENT_INSTANCES: u32 = 5;
826 
827     let mut pool = crate::small_pool_config();
828     pool.total_component_instances(TOTAL_COMPONENT_INSTANCES);
829     let mut config = Config::new();
830     config.wasm_component_model(true);
831     config.allocation_strategy(pool);
832 
833     let engine = Engine::new(&config)?;
834     let linker = wasmtime::component::Linker::new(&engine);
835     let component = wasmtime::component::Component::new(&engine, "(component)")?;
836 
837     let mut store = Store::new(&engine, ());
838     for _ in 0..TOTAL_COMPONENT_INSTANCES {
839         linker.instantiate(&mut store, &component)?;
840     }
841 
842     match linker.instantiate(&mut store, &component) {
843         Ok(_) => panic!("should have hit component instance limit"),
844         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
845     }
846 
847     drop(store);
848     let mut store = Store::new(&engine, ());
849     for _ in 0..TOTAL_COMPONENT_INSTANCES {
850         linker.instantiate(&mut store, &component)?;
851     }
852 
853     Ok(())
854 }
855 
856 #[test]
857 #[cfg(feature = "component-model")]
858 #[cfg(target_pointer_width = "64")] // error message tailored for 64-bit
component_instance_size_limit() -> Result<()>859 fn component_instance_size_limit() -> Result<()> {
860     let mut pool = crate::small_pool_config();
861     pool.max_component_instance_size(1);
862     let mut config = Config::new();
863     config.wasm_component_model(true);
864     config.allocation_strategy(pool);
865     let engine = Engine::new(&config)?;
866 
867     match wasmtime::component::Component::new(&engine, "(component)") {
868         Ok(_) => panic!("should have hit limit"),
869         Err(e) => {
870             e.assert_contains("instance allocation for this component requires 64 bytes");
871             e.assert_contains("which exceeds the configured maximum of 1 bytes");
872             e.assert_contains("`VMComponentContext` used 64 bytes");
873         }
874     }
875 
876     Ok(())
877 }
878 
879 #[test]
880 #[cfg_attr(miri, ignore)]
total_tables_limit() -> Result<()>881 fn total_tables_limit() -> Result<()> {
882     const TOTAL_TABLES: u32 = 5;
883 
884     let mut pool = crate::small_pool_config();
885     pool.total_tables(TOTAL_TABLES)
886         .total_core_instances(TOTAL_TABLES + 1);
887     let mut config = Config::new();
888     config.allocation_strategy(pool);
889 
890     let engine = Engine::new(&config)?;
891     let linker = Linker::new(&engine);
892     let module = Module::new(&engine, "(module (table 0 1 funcref))")?;
893 
894     let mut store = Store::new(&engine, ());
895     for _ in 0..TOTAL_TABLES {
896         linker.instantiate(&mut store, &module)?;
897     }
898 
899     match linker.instantiate(&mut store, &module) {
900         Ok(_) => panic!("should have hit table limit"),
901         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
902     }
903 
904     drop(store);
905     let mut store = Store::new(&engine, ());
906     for _ in 0..TOTAL_TABLES {
907         linker.instantiate(&mut store, &module)?;
908     }
909 
910     Ok(())
911 }
912 
913 #[tokio::test]
914 #[cfg(not(miri))]
total_stacks_limit() -> Result<()>915 async fn total_stacks_limit() -> Result<()> {
916     use super::async_functions::PollOnce;
917 
918     const TOTAL_STACKS: u32 = 2;
919 
920     let mut pool = crate::small_pool_config();
921     pool.total_stacks(TOTAL_STACKS)
922         .total_core_instances(TOTAL_STACKS + 1);
923     let mut config = Config::new();
924     config.allocation_strategy(pool);
925 
926     let engine = Engine::new(&config)?;
927 
928     let mut linker = Linker::new(&engine);
929     linker.func_new_async(
930         "async",
931         "yield",
932         FuncType::new(&engine, [], []),
933         |_caller, _params, _results| {
934             Box::new(async {
935                 tokio::task::yield_now().await;
936                 Ok(())
937             })
938         },
939     )?;
940 
941     let module = Module::new(
942         &engine,
943         r#"
944         (module
945             (import "async" "yield" (func $yield))
946             (func (export "run")
947                 call $yield
948             )
949 
950             (func $empty)
951             (start $empty)
952         )
953     "#,
954     )?;
955 
956     // Allocate stacks up to the limit. (Poll the futures once to make sure we
957     // actually enter Wasm and force a stack allocation.)
958 
959     let mut store1 = Store::new(&engine, ());
960     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
961     let run1 = instance1.get_func(&mut store1, "run").unwrap();
962     let future1 = PollOnce::new(Box::pin(run1.call_async(store1, &[], &mut [])))
963         .await
964         .unwrap_err();
965 
966     let mut store2 = Store::new(&engine, ());
967     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
968     let run2 = instance2.get_func(&mut store2, "run").unwrap();
969     let future2 = PollOnce::new(Box::pin(run2.call_async(store2, &[], &mut [])))
970         .await
971         .unwrap_err();
972 
973     // Allocating more should fail.
974     let mut store3 = Store::new(&engine, ());
975     match linker.instantiate_async(&mut store3, &module).await {
976         Ok(_) => panic!("should have hit stack limit"),
977         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
978     }
979 
980     // Finish the futures and return their Wasm stacks to the pool.
981     future1.await?;
982     future2.await?;
983 
984     // Should be able to allocate new stacks again.
985     let mut store1 = Store::new(&engine, ());
986     let instance1 = linker.instantiate_async(&mut store1, &module).await?;
987     let run1 = instance1.get_func(&mut store1, "run").unwrap();
988     let future1 = run1.call_async(&mut store1, &[], &mut []);
989 
990     let mut store2 = Store::new(&engine, ());
991     let instance2 = linker.instantiate_async(&mut store2, &module).await?;
992     let run2 = instance2.get_func(&mut store2, "run").unwrap();
993     let future2 = run2.call_async(&mut store2, &[], &mut []);
994 
995     future1.await?;
996     future2.await?;
997 
998     // Dispose one store via `Drop`, the other via `into_data`, and ensure that
999     // any lingering stacks make their way back to the pool.
1000     drop(store1);
1001     store2.into_data();
1002 
1003     Ok(())
1004 }
1005 
1006 #[test]
1007 #[cfg(feature = "component-model")]
component_core_instances_limit() -> Result<()>1008 fn component_core_instances_limit() -> Result<()> {
1009     let mut pool = crate::small_pool_config();
1010     pool.max_core_instances_per_component(1);
1011     let mut config = Config::new();
1012     config.wasm_component_model(true);
1013     config.allocation_strategy(pool);
1014     let engine = Engine::new(&config)?;
1015 
1016     // One core instance works.
1017     wasmtime::component::Component::new(
1018         &engine,
1019         r#"
1020             (component
1021                 (core module $m)
1022                 (core instance $a (instantiate $m))
1023             )
1024         "#,
1025     )?;
1026 
1027     // Two core instances doesn't.
1028     match wasmtime::component::Component::new(
1029         &engine,
1030         r#"
1031             (component
1032                 (core module $m)
1033                 (core instance $a (instantiate $m))
1034                 (core instance $b (instantiate $m))
1035             )
1036         "#,
1037     ) {
1038         Ok(_) => panic!("should have hit limit"),
1039         Err(e) => e.assert_contains(
1040             "The component transitively contains 2 core module instances, which exceeds the \
1041              configured maximum of 1",
1042         ),
1043     }
1044 
1045     Ok(())
1046 }
1047 
1048 #[test]
1049 #[cfg(feature = "component-model")]
component_memories_limit() -> Result<()>1050 fn component_memories_limit() -> Result<()> {
1051     let mut pool = crate::small_pool_config();
1052     pool.max_memories_per_component(1).total_memories(2);
1053     let mut config = Config::new();
1054     config.wasm_component_model(true);
1055     config.allocation_strategy(pool);
1056     let engine = Engine::new(&config)?;
1057 
1058     // One memory works.
1059     wasmtime::component::Component::new(
1060         &engine,
1061         r#"
1062             (component
1063                 (core module $m (memory 1 1))
1064                 (core instance $a (instantiate $m))
1065             )
1066         "#,
1067     )?;
1068 
1069     // Two memories doesn't.
1070     match wasmtime::component::Component::new(
1071         &engine,
1072         r#"
1073             (component
1074                 (core module $m (memory 1 1))
1075                 (core instance $a (instantiate $m))
1076                 (core instance $b (instantiate $m))
1077             )
1078         "#,
1079     ) {
1080         Ok(_) => panic!("should have hit limit"),
1081         Err(e) => e.assert_contains(
1082             "The component transitively contains 2 Wasm linear memories, which exceeds the \
1083              configured maximum of 1",
1084         ),
1085     }
1086 
1087     Ok(())
1088 }
1089 
1090 #[test]
1091 #[cfg(feature = "component-model")]
component_tables_limit() -> Result<()>1092 fn component_tables_limit() -> Result<()> {
1093     let mut pool = crate::small_pool_config();
1094     pool.max_tables_per_component(1).total_tables(2);
1095     let mut config = Config::new();
1096     config.wasm_component_model(true);
1097     config.allocation_strategy(pool);
1098     let engine = Engine::new(&config)?;
1099 
1100     // One table works.
1101     wasmtime::component::Component::new(
1102         &engine,
1103         r#"
1104             (component
1105                 (core module $m (table 1 1 funcref))
1106                 (core instance $a (instantiate $m))
1107             )
1108         "#,
1109     )?;
1110 
1111     // Two tables doesn't.
1112     match wasmtime::component::Component::new(
1113         &engine,
1114         r#"
1115             (component
1116                 (core module $m (table 1 1 funcref))
1117                 (core instance $a (instantiate $m))
1118                 (core instance $b (instantiate $m))
1119             )
1120         "#,
1121     ) {
1122         Ok(_) => panic!("should have hit limit"),
1123         Err(e) => e.assert_contains(
1124             "The component transitively contains 2 tables, which exceeds the \
1125              configured maximum of 1",
1126         ),
1127     }
1128 
1129     Ok(())
1130 }
1131 
1132 #[test]
1133 #[cfg(feature = "component-model")]
1134 #[cfg_attr(miri, ignore)]
component_core_instances_aggregate_size() -> Result<()>1135 fn component_core_instances_aggregate_size() -> Result<()> {
1136     let mut pool = crate::small_pool_config();
1137     pool.max_core_instances_per_component(100)
1138         // x86_64 requires 23824 bytes; we exceed this by a fair bit as there will
1139         // be differences by arch.
1140         .max_component_instance_size(1024);
1141 
1142     let mut config = Config::new();
1143     config.wasm_component_model(true);
1144     config.allocation_strategy(pool);
1145     let engine = Engine::new(&config)?;
1146 
1147     let core_instances = (1..100)
1148         .map(|i| format!("(core instance $i{i} (instantiate $m))"))
1149         .collect::<Vec<String>>()
1150         .join("\n");
1151 
1152     match wasmtime::component::Component::new(
1153         &engine,
1154         format!(
1155             "
1156             (component
1157                 (core module $m
1158                     (func (export \"f\") (result i32)
1159                         i32.const 42
1160                     )
1161                 )
1162                 {core_instances}
1163             )
1164         "
1165         ),
1166     ) {
1167         Ok(_) => panic!("should have hit aggregate size limit"),
1168         Err(e) => {
1169             e.assert_contains("instance allocation for this component requires");
1170             e.assert_contains("exceeds the configured maximum");
1171         }
1172     }
1173 
1174     Ok(())
1175 }
1176 
1177 #[test]
1178 #[cfg_attr(miri, ignore)]
total_memories_limit() -> Result<()>1179 fn total_memories_limit() -> Result<()> {
1180     const TOTAL_MEMORIES: u32 = 5;
1181 
1182     let mut pool = crate::small_pool_config();
1183     pool.total_memories(TOTAL_MEMORIES)
1184         .total_core_instances(TOTAL_MEMORIES + 1)
1185         .memory_protection_keys(Enabled::No);
1186     let mut config = Config::new();
1187     config.allocation_strategy(pool);
1188 
1189     let engine = Engine::new(&config)?;
1190     let linker = Linker::new(&engine);
1191     let module = Module::new(&engine, "(module (memory 1 1))")?;
1192 
1193     let mut store = Store::new(&engine, ());
1194     for _ in 0..TOTAL_MEMORIES {
1195         linker.instantiate(&mut store, &module)?;
1196     }
1197 
1198     match linker.instantiate(&mut store, &module) {
1199         Ok(_) => panic!("should have hit memory limit"),
1200         Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()),
1201     }
1202 
1203     drop(store);
1204     let mut store = Store::new(&engine, ());
1205     for _ in 0..TOTAL_MEMORIES {
1206         linker.instantiate(&mut store, &module)?;
1207     }
1208 
1209     Ok(())
1210 }
1211 
1212 #[test]
1213 #[cfg_attr(miri, ignore)]
decommit_batching() -> Result<()>1214 fn decommit_batching() -> Result<()> {
1215     for (capacity, batch_size) in [
1216         // A reasonable batch size.
1217         (10, 5),
1218         // Batch sizes of zero and one should effectively disable batching.
1219         (10, 1),
1220         (10, 0),
1221         // A bigger batch size than capacity, which forces the allocation path
1222         // to flush the decommit queue.
1223         (10, 99),
1224     ] {
1225         let mut pool = crate::small_pool_config();
1226         pool.total_memories(capacity)
1227             .total_core_instances(capacity)
1228             .decommit_batch_size(batch_size)
1229             .memory_protection_keys(Enabled::No);
1230         let mut config = Config::new();
1231         config.allocation_strategy(pool);
1232 
1233         let engine = Engine::new(&config)?;
1234         let linker = Linker::new(&engine);
1235         let module = Module::new(&engine, "(module (memory 1 1))")?;
1236 
1237         // Just make sure that we can instantiate all slots a few times and the
1238         // pooling allocator must be flushing the decommit queue as necessary.
1239         for _ in 0..3 {
1240             let mut store = Store::new(&engine, ());
1241             for _ in 0..capacity {
1242                 linker.instantiate(&mut store, &module)?;
1243             }
1244         }
1245     }
1246 
1247     Ok(())
1248 }
1249 
1250 #[test]
tricky_empty_table_with_empty_virtual_memory_alloc() -> Result<()>1251 fn tricky_empty_table_with_empty_virtual_memory_alloc() -> Result<()> {
1252     // Configure the pooling allocator to have no access to virtual memory, e.g.
1253     // no table elements but a single table. This should technically support a
1254     // single empty table being allocated into it but virtual memory isn't
1255     // actually allocated here.
1256     let mut cfg = PoolingAllocationConfig::default();
1257     cfg.table_elements(0);
1258     cfg.total_memories(0);
1259     cfg.total_tables(1);
1260     cfg.total_stacks(0);
1261     cfg.total_core_instances(1);
1262     cfg.max_memory_size(0);
1263 
1264     let mut c = Config::new();
1265     c.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1266 
1267     // Disable lazy init to actually try to get this to do something interesting
1268     // at runtime.
1269     c.table_lazy_init(false);
1270 
1271     let engine = Engine::new(&c)?;
1272 
1273     // This module has a single empty table, with a single empty element
1274     // segment. Nothing actually goes wrong here, it should instantiate
1275     // successfully. Along the way though the empty mmap above will get viewed
1276     // as an array-of-pointers, so everything internally should all line up to
1277     // work ok.
1278     let module = Module::new(
1279         &engine,
1280         r#"
1281 (module
1282     (table 0 funcref)
1283     (elem (i32.const 0) func)
1284 )
1285 "#,
1286     )?;
1287     let mut store = Store::new(&engine, ());
1288     Instance::new(&mut store, &module, &[])?;
1289     Ok(())
1290 }
1291 
1292 #[test]
1293 #[cfg_attr(miri, ignore)]
shared_memory_unsupported() -> Result<()>1294 fn shared_memory_unsupported() -> Result<()> {
1295     // Skip this test on platforms that don't support threads.
1296     if crate::threads::engine().is_none() {
1297         return Ok(());
1298     }
1299     let mut config = Config::new();
1300     let mut cfg = PoolingAllocationConfig::default();
1301     // shrink the size of this allocator
1302     cfg.total_memories(1);
1303     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1304     let engine = Engine::new(&config)?;
1305 
1306     let err = Module::new(
1307         &engine,
1308         r#"
1309             (module
1310                 (memory 5 5 shared)
1311             )
1312         "#,
1313     )
1314     .unwrap_err();
1315     err.assert_contains(
1316         "memory is shared which is not supported \
1317          in the pooling allocator",
1318     );
1319     err.assert_contains("memory index 0");
1320     Ok(())
1321 }
1322 
1323 #[test]
1324 #[cfg_attr(miri, ignore)]
custom_page_sizes_reusing_same_slot() -> Result<()>1325 fn custom_page_sizes_reusing_same_slot() -> Result<()> {
1326     let mut config = Config::new();
1327     config.wasm_custom_page_sizes(true);
1328     let mut cfg = crate::small_pool_config();
1329     // force the memories below to collide in the same memory slot
1330     cfg.total_memories(1);
1331     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1332     let engine = Engine::new(&config)?;
1333 
1334     // Instantiate one module, leaving the slot 5 bytes big (but one page
1335     // accessible)
1336     {
1337         let m1 = Module::new(
1338             &engine,
1339             r#"
1340                 (module
1341                     (memory 5 (pagesize 1))
1342 
1343                     (data (i32.const 0) "a")
1344                 )
1345             "#,
1346         )?;
1347         let mut store = Store::new(&engine, ());
1348         Instance::new(&mut store, &m1, &[])?;
1349     }
1350 
1351     // Instantiate a second module, which should work
1352     {
1353         let m2 = Module::new(
1354             &engine,
1355             r#"
1356                 (module
1357                     (memory 6 (pagesize 1))
1358 
1359                     (data (i32.const 0) "a")
1360                 )
1361             "#,
1362         )?;
1363         let mut store = Store::new(&engine, ());
1364         Instance::new(&mut store, &m2, &[])?;
1365     }
1366     Ok(())
1367 }
1368 
1369 #[test]
1370 #[cfg_attr(miri, ignore)]
instantiate_non_page_aligned_sizes() -> Result<()>1371 fn instantiate_non_page_aligned_sizes() -> Result<()> {
1372     let mut config = Config::new();
1373     config.wasm_custom_page_sizes(true);
1374     let mut cfg = crate::small_pool_config();
1375     cfg.total_memories(1);
1376     cfg.max_memory_size(761927);
1377     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1378     let engine = Engine::new(&config)?;
1379 
1380     let module = Module::new(
1381         &engine,
1382         r#"
1383             (module
1384               (memory 761927 761927 (pagesize 0x1))
1385             )
1386         "#,
1387     )?;
1388     let mut store = Store::new(&engine, ());
1389     Instance::new(&mut store, &module, &[])?;
1390     Ok(())
1391 }
1392 
1393 #[test]
1394 #[cfg_attr(miri, ignore)]
pagemap_scan_enabled_or_disabled() -> Result<()>1395 fn pagemap_scan_enabled_or_disabled() -> Result<()> {
1396     let mut config = Config::new();
1397     let mut cfg = crate::small_pool_config();
1398     cfg.total_memories(1);
1399     cfg.pagemap_scan(Enabled::Yes);
1400     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1401     let result = Engine::new(&config);
1402 
1403     if PoolingAllocationConfig::is_pagemap_scan_available() {
1404         assert!(result.is_ok());
1405     } else {
1406         assert!(result.is_err());
1407     }
1408     Ok(())
1409 }
1410 
1411 #[test]
1412 #[cfg_attr(miri, ignore)]
pooling_reuse_resets() -> Result<()>1413 fn pooling_reuse_resets() -> Result<()> {
1414     let mut config = Config::new();
1415     let mut cfg = crate::small_pool_config();
1416     cfg.total_memories(1);
1417     cfg.max_memory_size(0x2000000);
1418     config.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
1419     config.memory_guard_size(0);
1420     config.memory_reservation(0x2000000);
1421     let engine = Engine::new(&config)?;
1422 
1423     let a = Module::new(&engine, r#"(module (memory (export "m") 10 100))"#)?;
1424     let b = Module::new(
1425         &engine,
1426         r#"
1427 (module $B
1428   (memory 5 100)
1429   (func (export "read_oob") (result i32)
1430     (i32.load (i32.const 983040))
1431   )
1432 )
1433         "#,
1434     )?;
1435 
1436     {
1437         let mut store = Store::new(&engine, ());
1438         let instance = Instance::new(&mut store, &a, &[])?;
1439         let memory = instance.get_memory(&mut store, "m").unwrap();
1440         memory.grow(&mut store, 10)?;
1441     }
1442 
1443     {
1444         let mut store = Store::new(&engine, ());
1445         let instance = Instance::new(&mut store, &b, &[])?;
1446         let read_oob = instance.get_typed_func::<(), i32>(&mut store, "read_oob")?;
1447         let trap: Trap = read_oob.call(&mut store, ()).unwrap_err().downcast()?;
1448         assert_eq!(trap, Trap::MemoryOutOfBounds);
1449     }
1450 
1451     Ok(())
1452 }
1453 
1454 // This test instantiates a memory with an image into a slot in the pooling
1455 // allocator in a way that maps the image into the allocator but fails
1456 // instantiation. Failure here is injected with `ResourceLimiter`. Afterwards
1457 // instantiation is allowed to succeed with a memory that has no image, and this
1458 // asserts that the previous image is indeed not available any more as that
1459 // would otherwise mean data was leaked between modules.
1460 #[test]
memory_reset_if_instantiation_fails() -> Result<()>1461 fn memory_reset_if_instantiation_fails() -> Result<()> {
1462     struct Limiter;
1463 
1464     impl ResourceLimiter for Limiter {
1465         fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> {
1466             Ok(false)
1467         }
1468 
1469         fn table_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> {
1470             Ok(false)
1471         }
1472     }
1473 
1474     let pool = crate::small_pool_config();
1475     let mut config = Config::new();
1476     config.allocation_strategy(pool);
1477     let engine = Engine::new(&config)?;
1478 
1479     let module_with_image = Module::new(
1480         &engine,
1481         r#"
1482             (module
1483                 (memory 1)
1484                 (data (i32.const 0) "\aa")
1485             )
1486         "#,
1487     )?;
1488     let module_without_image = Module::new(
1489         &engine,
1490         r#"
1491             (module
1492                 (memory (export "m") 1)
1493             )
1494         "#,
1495     )?;
1496 
1497     let mut store = Store::new(&engine, Limiter);
1498     store.limiter(|s| s);
1499     assert!(Instance::new(&mut store, &module_with_image, &[]).is_err());
1500     drop(store);
1501 
1502     let mut store = Store::new(&engine, Limiter);
1503     let instance = Instance::new(&mut store, &module_without_image, &[])?;
1504     let mem = instance.get_memory(&mut store, "m").unwrap();
1505     let data = mem.data(&store);
1506     assert_eq!(data[0], 0);
1507 
1508     Ok(())
1509 }
1510 
1511 #[test]
purge_module_with_mpk() -> Result<()>1512 fn purge_module_with_mpk() -> Result<()> {
1513     if !wasmtime::PoolingAllocationConfig::are_memory_protection_keys_available() {
1514         println!("skipping test; mpk is not supported");
1515         return Ok(());
1516     }
1517 
1518     let mut pool = crate::small_pool_config();
1519     pool.total_memories(2)
1520         .memory_protection_keys(Enabled::Yes)
1521         .max_memory_protection_keys(2);
1522     let mut config = Config::new();
1523     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1524     let engine = Engine::new(&config)?;
1525 
1526     // Create a module and instantiate it in stripe 0.
1527     let m1 = Module::new(&engine, "(module (memory 1))")?;
1528     let mut store = Store::new(&engine, ());
1529     Instance::new(&mut store, &m1, &[])?;
1530 
1531     // Create and instantiate a module in stripe 1. Note that the store is
1532     // immediately destroyed here after instantiation. That should leave the
1533     // linear memory slot available for re-instantiation.
1534     {
1535         let m2 = Module::new(&engine, "(module (memory 1))")?;
1536         Instance::new(&mut Store::new(&engine, ()), &m2, &[])?;
1537 
1538         // ... drop `m2` here which will purge it and should remove the warm
1539         // slot left for the module. Nothing should get corrupted...
1540     }
1541 
1542     // Drop the outer store here which will clean up the rest of the
1543     // instance/etc.
1544     drop(store);
1545 
1546     Ok(())
1547 }
1548