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