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