1 use super::{ErrorExt, skip_pooling_allocator_tests}; 2 use wasmtime::*; 3 4 #[test] 5 fn successful_instantiation() -> Result<()> { 6 let pool = crate::small_pool_config(); 7 let mut config = Config::new(); 8 config.allocation_strategy(pool); 9 config.memory_guard_size(0); 10 config.memory_reservation(1 << 16); 11 12 let engine = Engine::new(&config)?; 13 let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?; 14 15 // Module should instantiate 16 let mut store = Store::new(&engine, ()); 17 Instance::new(&mut store, &module, &[])?; 18 19 Ok(()) 20 } 21 22 #[test] 23 #[cfg_attr(miri, ignore)] 24 fn memory_limit() -> Result<()> { 25 let mut pool = crate::small_pool_config(); 26 pool.max_memory_size(3 << 16); 27 let mut config = Config::new(); 28 config.allocation_strategy(pool); 29 config.memory_guard_size(1 << 16); 30 config.memory_reservation(3 << 16); 31 config.wasm_multi_memory(true); 32 33 let engine = Engine::new(&config)?; 34 35 // Module should fail to instantiate because it has too many memories 36 match Module::new(&engine, r#"(module (memory 1) (memory 1))"#) { 37 Ok(_) => panic!("module instantiation should fail"), 38 Err(e) => { 39 e.assert_contains("defined memories count of 2 exceeds the per-instance limit of 1") 40 } 41 } 42 43 // Module should fail to instantiate because the minimum is greater than 44 // the configured limit 45 match Module::new(&engine, r#"(module (memory 4))"#) { 46 Ok(_) => panic!("module instantiation should fail"), 47 Err(e) => { 48 e.assert_contains( 49 "memory index 0 is unsupported in this pooling allocator \ 50 configuration", 51 ); 52 e.assert_contains( 53 "memory has a minimum byte size of 262144 which exceeds \ 54 the limit of 0x30000 bytes", 55 ); 56 } 57 } 58 59 let module = Module::new( 60 &engine, 61 r#"(module (memory (export "m") 0) (func (export "f") (result i32) (memory.grow (i32.const 1))))"#, 62 )?; 63 64 // Instantiate the module and grow the memory via the `f` function 65 { 66 let mut store = Store::new(&engine, ()); 67 let instance = Instance::new(&mut store, &module, &[])?; 68 let f = instance.get_typed_func::<(), i32>(&mut store, "f")?; 69 70 assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 0); 71 assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 1); 72 assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 2); 73 assert_eq!( 74 f.call(&mut store, ()).expect("function should not trap"), 75 -1 76 ); 77 assert_eq!( 78 f.call(&mut store, ()).expect("function should not trap"), 79 -1 80 ); 81 } 82 83 // Instantiate the module and grow the memory via the Wasmtime API 84 let mut store = Store::new(&engine, ()); 85 let instance = Instance::new(&mut store, &module, &[])?; 86 87 let memory = instance.get_memory(&mut store, "m").unwrap(); 88 assert_eq!(memory.size(&store), 0); 89 assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 0); 90 assert_eq!(memory.size(&store), 1); 91 assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 1); 92 assert_eq!(memory.size(&store), 2); 93 assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 2); 94 assert_eq!(memory.size(&store), 3); 95 assert!(memory.grow(&mut store, 1).is_err()); 96 97 Ok(()) 98 } 99 100 #[test] 101 fn memory_init() -> Result<()> { 102 let mut pool = crate::small_pool_config(); 103 pool.max_memory_size(2 << 16).table_elements(0); 104 let mut config = Config::new(); 105 config.allocation_strategy(pool); 106 107 let engine = Engine::new(&config)?; 108 109 let module = Module::new( 110 &engine, 111 r#" 112 (module 113 (memory (export "m") 2) 114 (data (i32.const 65530) "this data spans multiple pages") 115 (data (i32.const 10) "hello world") 116 ) 117 "#, 118 )?; 119 120 let mut store = Store::new(&engine, ()); 121 let instance = Instance::new(&mut store, &module, &[])?; 122 let memory = instance.get_memory(&mut store, "m").unwrap(); 123 124 assert_eq!( 125 &memory.data(&store)[65530..65560], 126 b"this data spans multiple pages" 127 ); 128 assert_eq!(&memory.data(&store)[10..21], b"hello world"); 129 130 Ok(()) 131 } 132 133 #[test] 134 #[cfg_attr(miri, ignore)] 135 fn memory_guard_page_trap() -> Result<()> { 136 let mut pool = crate::small_pool_config(); 137 pool.max_memory_size(2 << 16).table_elements(0); 138 let mut config = Config::new(); 139 config.allocation_strategy(pool); 140 141 let engine = Engine::new(&config)?; 142 143 let module = Module::new( 144 &engine, 145 r#" 146 (module 147 (memory (export "m") 0) 148 (func (export "f") (param i32) local.get 0 i32.load drop) 149 ) 150 "#, 151 )?; 152 153 // Instantiate the module and check for out of bounds trap 154 for _ in 0..10 { 155 let mut store = Store::new(&engine, ()); 156 let instance = Instance::new(&mut store, &module, &[])?; 157 let m = instance.get_memory(&mut store, "m").unwrap(); 158 let f = instance.get_typed_func::<i32, ()>(&mut store, "f")?; 159 160 let trap = f 161 .call(&mut store, 0) 162 .expect_err("function should trap") 163 .downcast::<Trap>()?; 164 assert_eq!(trap, Trap::MemoryOutOfBounds); 165 166 let trap = f 167 .call(&mut store, 1) 168 .expect_err("function should trap") 169 .downcast::<Trap>()?; 170 assert_eq!(trap, Trap::MemoryOutOfBounds); 171 172 m.grow(&mut store, 1).expect("memory should grow"); 173 f.call(&mut store, 0).expect("function should not trap"); 174 175 let trap = f 176 .call(&mut store, 65536) 177 .expect_err("function should trap") 178 .downcast::<Trap>()?; 179 assert_eq!(trap, Trap::MemoryOutOfBounds); 180 181 let trap = f 182 .call(&mut store, 65537) 183 .expect_err("function should trap") 184 .downcast::<Trap>()?; 185 assert_eq!(trap, Trap::MemoryOutOfBounds); 186 187 m.grow(&mut store, 1).expect("memory should grow"); 188 f.call(&mut store, 65536).expect("function should not trap"); 189 190 m.grow(&mut store, 1) 191 .expect_err("memory should be at the limit"); 192 } 193 194 Ok(()) 195 } 196 197 #[test] 198 fn memory_zeroed() -> Result<()> { 199 if skip_pooling_allocator_tests() { 200 return Ok(()); 201 } 202 203 let mut pool = crate::small_pool_config(); 204 pool.max_memory_size(1 << 16).table_elements(0); 205 let mut config = Config::new(); 206 config.allocation_strategy(pool); 207 config.memory_guard_size(0); 208 config.memory_reservation(1 << 16); 209 210 let engine = Engine::new(&config)?; 211 212 let module = Module::new(&engine, r#"(module (memory (export "m") 1))"#)?; 213 214 // Instantiate the module repeatedly after writing data to the entire memory 215 for _ in 0..10 { 216 let mut store = Store::new(&engine, ()); 217 let instance = Instance::new(&mut store, &module, &[])?; 218 let memory = instance.get_memory(&mut store, "m").unwrap(); 219 220 assert_eq!(memory.size(&store,), 1); 221 assert_eq!(memory.data_size(&store), 65536); 222 223 let ptr = memory.data_mut(&mut store).as_mut_ptr(); 224 225 unsafe { 226 for i in 0..8192 { 227 assert_eq!(*ptr.cast::<u64>().offset(i), 0); 228 } 229 std::ptr::write_bytes(ptr, 0xFE, memory.data_size(&store)); 230 } 231 } 232 233 Ok(()) 234 } 235 236 #[test] 237 #[cfg_attr(miri, ignore)] 238 fn table_limit() -> Result<()> { 239 const TABLE_ELEMENTS: usize = 10; 240 let mut pool = crate::small_pool_config(); 241 pool.table_elements(TABLE_ELEMENTS); 242 let mut config = Config::new(); 243 config.allocation_strategy(pool); 244 config.memory_guard_size(0); 245 config.memory_reservation(1 << 16); 246 247 let engine = Engine::new(&config)?; 248 249 // Module should fail to instantiate because it has too many tables 250 match Module::new(&engine, r#"(module (table 1 funcref) (table 1 funcref))"#) { 251 Ok(_) => panic!("module compilation should fail"), 252 Err(e) => { 253 e.assert_contains("defined tables count of 2 exceeds the per-instance limit of 1") 254 } 255 } 256 257 // Module should fail to instantiate because the minimum is greater than 258 // the configured limit 259 match Module::new(&engine, r#"(module (table 31 funcref))"#) { 260 Ok(_) => panic!("module compilation should fail"), 261 Err(e) => e.assert_contains( 262 "table index 0 has a minimum element size of 31 which exceeds the limit of 10", 263 ), 264 } 265 266 let module = Module::new( 267 &engine, 268 r#"(module (table (export "t") 0 funcref) (func (export "f") (result i32) (table.grow (ref.null func) (i32.const 1))))"#, 269 )?; 270 271 // Instantiate the module and grow the table via the `f` function 272 { 273 let mut store = Store::new(&engine, ()); 274 let instance = Instance::new(&mut store, &module, &[])?; 275 let f = instance.get_typed_func::<(), i32>(&mut store, "f")?; 276 277 for i in 0..TABLE_ELEMENTS { 278 assert_eq!( 279 f.call(&mut store, ()).expect("function should not trap"), 280 i as i32 281 ); 282 } 283 284 assert_eq!( 285 f.call(&mut store, ()).expect("function should not trap"), 286 -1 287 ); 288 assert_eq!( 289 f.call(&mut store, ()).expect("function should not trap"), 290 -1 291 ); 292 } 293 294 // Instantiate the module and grow the table via the Wasmtime API 295 let mut store = Store::new(&engine, ()); 296 let instance = Instance::new(&mut store, &module, &[])?; 297 298 let table = instance.get_table(&mut store, "t").unwrap(); 299 300 for i in 0..TABLE_ELEMENTS { 301 assert_eq!(table.size(&store), i as u64); 302 assert_eq!( 303 table 304 .grow(&mut store, 1, Ref::Func(None)) 305 .expect("table should grow"), 306 i as u64 307 ); 308 } 309 310 assert_eq!(table.size(&store), TABLE_ELEMENTS as u64); 311 assert!(table.grow(&mut store, 1, Ref::Func(None)).is_err()); 312 313 Ok(()) 314 } 315 316 #[test] 317 #[cfg_attr(miri, ignore)] 318 fn table_init() -> Result<()> { 319 let mut pool = crate::small_pool_config(); 320 pool.max_memory_size(0).table_elements(6); 321 let mut config = Config::new(); 322 config.allocation_strategy(pool); 323 324 let engine = Engine::new(&config)?; 325 326 let module = Module::new( 327 &engine, 328 r#" 329 (module 330 (table (export "t") 6 funcref) 331 (elem (i32.const 1) 1 2 3 4) 332 (elem (i32.const 0) 0) 333 (func) 334 (func (param i32)) 335 (func (param i32 i32)) 336 (func (param i32 i32 i32)) 337 (func (param i32 i32 i32 i32)) 338 ) 339 "#, 340 )?; 341 342 let mut store = Store::new(&engine, ()); 343 let instance = Instance::new(&mut store, &module, &[])?; 344 let table = instance.get_table(&mut store, "t").unwrap(); 345 346 for i in 0..5 { 347 let v = table.get(&mut store, i).expect("table should have entry"); 348 let f = v 349 .as_func() 350 .expect("expected funcref") 351 .expect("expected non-null value"); 352 assert_eq!(f.ty(&store).params().len(), i as usize); 353 } 354 355 assert!( 356 table 357 .get(&mut store, 5) 358 .expect("table should have entry") 359 .as_func() 360 .expect("expected funcref") 361 .is_none(), 362 "funcref should be null" 363 ); 364 365 Ok(()) 366 } 367 368 #[test] 369 fn table_zeroed() -> Result<()> { 370 if skip_pooling_allocator_tests() { 371 return Ok(()); 372 } 373 374 let pool = crate::small_pool_config(); 375 let mut config = Config::new(); 376 config.allocation_strategy(pool); 377 config.memory_guard_size(0); 378 config.memory_reservation(1 << 16); 379 380 let engine = Engine::new(&config)?; 381 382 let module = Module::new(&engine, r#"(module (table (export "t") 10 funcref))"#)?; 383 384 // Instantiate the module repeatedly after filling table elements 385 for _ in 0..10 { 386 let mut store = Store::new(&engine, ()); 387 let instance = Instance::new(&mut store, &module, &[])?; 388 let table = instance.get_table(&mut store, "t").unwrap(); 389 let f = Func::wrap(&mut store, || {}); 390 391 assert_eq!(table.size(&store), 10); 392 393 for i in 0..10 { 394 match table.get(&mut store, i).unwrap() { 395 Ref::Func(r) => assert!(r.is_none()), 396 _ => panic!("expected a funcref"), 397 } 398 table.set(&mut store, i, Ref::Func(Some(f))).unwrap(); 399 } 400 } 401 402 Ok(()) 403 } 404 405 #[test] 406 fn total_core_instances_limit() -> Result<()> { 407 const INSTANCE_LIMIT: u32 = 10; 408 let mut pool = crate::small_pool_config(); 409 pool.total_core_instances(INSTANCE_LIMIT); 410 let mut config = Config::new(); 411 config.allocation_strategy(pool); 412 config.memory_guard_size(0); 413 config.memory_reservation(1 << 16); 414 415 let engine = Engine::new(&config)?; 416 let module = Module::new(&engine, r#"(module)"#)?; 417 418 // Instantiate to the limit 419 { 420 let mut store = Store::new(&engine, ()); 421 422 for _ in 0..INSTANCE_LIMIT { 423 Instance::new(&mut store, &module, &[])?; 424 } 425 426 match Instance::new(&mut store, &module, &[]) { 427 Ok(_) => panic!("instantiation should fail"), 428 Err(e) => assert!(e.is::<PoolConcurrencyLimitError>()), 429 } 430 } 431 432 // With the above store dropped, ensure instantiations can be made 433 434 let mut store = Store::new(&engine, ()); 435 436 for _ in 0..INSTANCE_LIMIT { 437 Instance::new(&mut store, &module, &[])?; 438 } 439 440 Ok(()) 441 } 442 443 #[test] 444 fn preserve_data_segments() -> Result<()> { 445 let mut pool = crate::small_pool_config(); 446 pool.total_memories(2); 447 let mut config = Config::new(); 448 config.allocation_strategy(pool); 449 let engine = Engine::new(&config)?; 450 let m = Module::new( 451 &engine, 452 r#" 453 (module 454 (memory (export "mem") 1 1) 455 (data (i32.const 0) "foo")) 456 "#, 457 )?; 458 let mut store = Store::new(&engine, ()); 459 let i = Instance::new(&mut store, &m, &[])?; 460 461 // Drop the module. This should *not* drop the actual data referenced by the 462 // module. 463 drop(m); 464 465 // Spray some stuff on the heap. If wasm data lived on the heap this should 466 // paper over things and help us catch use-after-free here if it would 467 // otherwise happen. 468 if !cfg!(miri) { 469 let mut strings = Vec::new(); 470 for _ in 0..1000 { 471 let mut string = String::new(); 472 for _ in 0..1000 { 473 string.push('g'); 474 } 475 strings.push(string); 476 } 477 drop(strings); 478 } 479 480 let mem = i.get_memory(&mut store, "mem").unwrap(); 481 482 // Hopefully it's still `foo`! 483 assert!(mem.data(&store).starts_with(b"foo")); 484 485 Ok(()) 486 } 487 488 #[test] 489 fn multi_memory_with_imported_memories() -> Result<()> { 490 // This test checks that the base address for the defined memory is correct for the instance 491 // despite the presence of an imported memory. 492 493 let mut pool = crate::small_pool_config(); 494 pool.total_memories(2).max_memories_per_module(2); 495 let mut config = Config::new(); 496 config.allocation_strategy(pool); 497 config.wasm_multi_memory(true); 498 499 let engine = Engine::new(&config)?; 500 let module = Module::new( 501 &engine, 502 r#"(module (import "" "m1" (memory 0)) (memory (export "m2") 1))"#, 503 )?; 504 505 let mut store = Store::new(&engine, ()); 506 507 let m1 = Memory::new(&mut store, MemoryType::new(0, None))?; 508 let instance = Instance::new(&mut store, &module, &[m1.into()])?; 509 510 let m2 = instance.get_memory(&mut store, "m2").unwrap(); 511 512 m2.data_mut(&mut store)[0] = 0x42; 513 assert_eq!(m2.data(&store)[0], 0x42); 514 515 Ok(()) 516 } 517 518 #[test] 519 fn drop_externref_global_during_module_init() -> Result<()> { 520 struct Limiter; 521 522 impl ResourceLimiter for Limiter { 523 fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> { 524 Ok(false) 525 } 526 527 fn table_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> { 528 Ok(false) 529 } 530 } 531 532 let pool = crate::small_pool_config(); 533 let mut config = Config::new(); 534 config.wasm_reference_types(true); 535 config.allocation_strategy(pool); 536 537 let engine = Engine::new(&config)?; 538 539 let module = Module::new( 540 &engine, 541 r#" 542 (module 543 (global i32 (i32.const 1)) 544 (global i32 (i32.const 2)) 545 (global i32 (i32.const 3)) 546 (global i32 (i32.const 4)) 547 (global i32 (i32.const 5)) 548 ) 549 "#, 550 )?; 551 552 let mut store = Store::new(&engine, Limiter); 553 Instance::new(&mut store, &module, &[])?; 554 drop(store); 555 556 let module = Module::new( 557 &engine, 558 r#" 559 (module 560 (memory 1) 561 (global (mut externref) (ref.null extern)) 562 ) 563 "#, 564 )?; 565 566 let mut store = Store::new(&engine, Limiter); 567 store.limiter(|s| s); 568 assert!(Instance::new(&mut store, &module, &[]).is_err()); 569 570 Ok(()) 571 } 572 573 #[test] 574 #[cfg_attr(miri, ignore)] 575 fn switch_image_and_non_image() -> Result<()> { 576 let pool = crate::small_pool_config(); 577 let mut c = Config::new(); 578 c.allocation_strategy(pool); 579 let engine = Engine::new(&c)?; 580 let module1 = Module::new( 581 &engine, 582 r#" 583 (module 584 (memory 1) 585 (func (export "load") (param i32) (result i32) 586 local.get 0 587 i32.load 588 ) 589 ) 590 "#, 591 )?; 592 let module2 = Module::new( 593 &engine, 594 r#" 595 (module 596 (memory (export "memory") 1) 597 (data (i32.const 0) "1234") 598 ) 599 "#, 600 )?; 601 602 let assert_zero = || -> Result<()> { 603 let mut store = Store::new(&engine, ()); 604 let instance = Instance::new(&mut store, &module1, &[])?; 605 let func = instance.get_typed_func::<i32, i32>(&mut store, "load")?; 606 assert_eq!(func.call(&mut store, 0)?, 0); 607 Ok(()) 608 }; 609 610 // Initialize with a heap image and make sure the next instance, without an 611 // image, is zeroed 612 Instance::new(&mut Store::new(&engine, ()), &module2, &[])?; 613 assert_zero()?; 614 615 // ... transition back to heap image and do this again 616 Instance::new(&mut Store::new(&engine, ()), &module2, &[])?; 617 assert_zero()?; 618 619 // And go back to an image and make sure it's read/write on the host. 620 let mut store = Store::new(&engine, ()); 621 let instance = Instance::new(&mut store, &module2, &[])?; 622 let memory = instance.get_memory(&mut store, "memory").unwrap(); 623 let mem = memory.data_mut(&mut store); 624 assert!(mem.starts_with(b"1234")); 625 mem[..6].copy_from_slice(b"567890"); 626 627 Ok(()) 628 } 629 630 #[test] 631 #[cfg(target_pointer_width = "64")] 632 #[cfg_attr(miri, ignore)] 633 fn instance_too_large() -> Result<()> { 634 let mut pool = crate::small_pool_config(); 635 pool.max_core_instance_size(16); 636 let mut config = Config::new(); 637 config.allocation_strategy(pool); 638 639 let engine = Engine::new(&config)?; 640 match Module::new(&engine, "(module)") { 641 Ok(_) => panic!("should have failed to compile"), 642 Err(e) => { 643 e.assert_contains("exceeds the configured maximum of 16 bytes"); 644 e.assert_contains("breakdown of allocation requirement"); 645 e.assert_contains("instance state management"); 646 e.assert_contains("static vmctx data"); 647 } 648 } 649 650 let mut lots_of_globals = format!("(module"); 651 for _ in 0..100 { 652 lots_of_globals.push_str("(global i32 i32.const 0)\n"); 653 } 654 lots_of_globals.push_str(")"); 655 656 match Module::new(&engine, &lots_of_globals) { 657 Ok(_) => panic!("should have failed to compile"), 658 Err(e) => { 659 e.assert_contains("exceeds the configured maximum of 16 bytes"); 660 e.assert_contains("breakdown of allocation requirement"); 661 e.assert_contains("defined globals"); 662 e.assert_contains("instance state management"); 663 } 664 } 665 666 Ok(()) 667 } 668 669 #[test] 670 #[cfg_attr(miri, ignore)] 671 fn dynamic_memory_pooling_allocator() -> Result<()> { 672 for guard_size in [0, 1 << 16] { 673 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)] 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")] 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 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)] 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))] 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")] 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")] 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")] 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)] 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)] 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)] 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] 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)] 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)] 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)] 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)] 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)] 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] 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] 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