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