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(65536); 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.memory_pages(3); 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(65536); 32 config.static_memory_maximum_size(3 * 65536); 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 page size of 4 which exceeds the limit of 3", 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.memory_pages(2).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.memory_pages(2).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.memory_pages(1).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(65536); 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(65536); 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.memory_pages(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(65536); 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(65536); 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_eq!( 434 e.to_string(), 435 format!( 436 "maximum concurrent core instance limit of {} reached", 437 INSTANCE_LIMIT 438 ) 439 ), 440 } 441 } 442 443 // With the above store dropped, ensure instantiations can be made 444 445 let mut store = Store::new(&engine, ()); 446 447 for _ in 0..INSTANCE_LIMIT { 448 Instance::new(&mut store, &module, &[])?; 449 } 450 451 Ok(()) 452 } 453 454 #[test] 455 fn preserve_data_segments() -> Result<()> { 456 let mut pool = crate::small_pool_config(); 457 pool.total_memories(2); 458 let mut config = Config::new(); 459 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 460 let engine = Engine::new(&config)?; 461 let m = Module::new( 462 &engine, 463 r#" 464 (module 465 (memory (export "mem") 1 1) 466 (data (i32.const 0) "foo")) 467 "#, 468 )?; 469 let mut store = Store::new(&engine, ()); 470 let i = Instance::new(&mut store, &m, &[])?; 471 472 // Drop the module. This should *not* drop the actual data referenced by the 473 // module. 474 drop(m); 475 476 // Spray some stuff on the heap. If wasm data lived on the heap this should 477 // paper over things and help us catch use-after-free here if it would 478 // otherwise happen. 479 if !cfg!(miri) { 480 let mut strings = Vec::new(); 481 for _ in 0..1000 { 482 let mut string = String::new(); 483 for _ in 0..1000 { 484 string.push('g'); 485 } 486 strings.push(string); 487 } 488 drop(strings); 489 } 490 491 let mem = i.get_memory(&mut store, "mem").unwrap(); 492 493 // Hopefully it's still `foo`! 494 assert!(mem.data(&store).starts_with(b"foo")); 495 496 Ok(()) 497 } 498 499 #[test] 500 fn multi_memory_with_imported_memories() -> Result<()> { 501 // This test checks that the base address for the defined memory is correct for the instance 502 // despite the presence of an imported memory. 503 504 let mut pool = crate::small_pool_config(); 505 pool.total_memories(2).max_memories_per_module(2); 506 let mut config = Config::new(); 507 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 508 config.wasm_multi_memory(true); 509 510 let engine = Engine::new(&config)?; 511 let module = Module::new( 512 &engine, 513 r#"(module (import "" "m1" (memory 0)) (memory (export "m2") 1))"#, 514 )?; 515 516 let mut store = Store::new(&engine, ()); 517 518 let m1 = Memory::new(&mut store, MemoryType::new(0, None))?; 519 let instance = Instance::new(&mut store, &module, &[m1.into()])?; 520 521 let m2 = instance.get_memory(&mut store, "m2").unwrap(); 522 523 m2.data_mut(&mut store)[0] = 0x42; 524 assert_eq!(m2.data(&store)[0], 0x42); 525 526 Ok(()) 527 } 528 529 #[test] 530 fn drop_externref_global_during_module_init() -> Result<()> { 531 struct Limiter; 532 533 impl ResourceLimiter for Limiter { 534 fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> Result<bool> { 535 Ok(false) 536 } 537 538 fn table_growing(&mut self, _: u32, _: u32, _: Option<u32>) -> Result<bool> { 539 Ok(false) 540 } 541 } 542 543 let pool = crate::small_pool_config(); 544 let mut config = Config::new(); 545 config.wasm_reference_types(true); 546 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 547 548 let engine = Engine::new(&config)?; 549 550 let module = Module::new( 551 &engine, 552 r#" 553 (module 554 (global i32 (i32.const 1)) 555 (global i32 (i32.const 2)) 556 (global i32 (i32.const 3)) 557 (global i32 (i32.const 4)) 558 (global i32 (i32.const 5)) 559 ) 560 "#, 561 )?; 562 563 let mut store = Store::new(&engine, Limiter); 564 Instance::new(&mut store, &module, &[])?; 565 drop(store); 566 567 let module = Module::new( 568 &engine, 569 r#" 570 (module 571 (memory 1) 572 (global (mut externref) (ref.null extern)) 573 ) 574 "#, 575 )?; 576 577 let mut store = Store::new(&engine, Limiter); 578 store.limiter(|s| s); 579 assert!(Instance::new(&mut store, &module, &[]).is_err()); 580 581 Ok(()) 582 } 583 584 #[test] 585 #[cfg_attr(miri, ignore)] 586 fn switch_image_and_non_image() -> Result<()> { 587 let pool = crate::small_pool_config(); 588 let mut c = Config::new(); 589 c.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 590 let engine = Engine::new(&c)?; 591 let module1 = Module::new( 592 &engine, 593 r#" 594 (module 595 (memory 1) 596 (func (export "load") (param i32) (result i32) 597 local.get 0 598 i32.load 599 ) 600 ) 601 "#, 602 )?; 603 let module2 = Module::new( 604 &engine, 605 r#" 606 (module 607 (memory (export "memory") 1) 608 (data (i32.const 0) "1234") 609 ) 610 "#, 611 )?; 612 613 let assert_zero = || -> Result<()> { 614 let mut store = Store::new(&engine, ()); 615 let instance = Instance::new(&mut store, &module1, &[])?; 616 let func = instance.get_typed_func::<i32, i32>(&mut store, "load")?; 617 assert_eq!(func.call(&mut store, 0)?, 0); 618 Ok(()) 619 }; 620 621 // Initialize with a heap image and make sure the next instance, without an 622 // image, is zeroed 623 Instance::new(&mut Store::new(&engine, ()), &module2, &[])?; 624 assert_zero()?; 625 626 // ... transition back to heap image and do this again 627 Instance::new(&mut Store::new(&engine, ()), &module2, &[])?; 628 assert_zero()?; 629 630 // And go back to an image and make sure it's read/write on the host. 631 let mut store = Store::new(&engine, ()); 632 let instance = Instance::new(&mut store, &module2, &[])?; 633 let memory = instance.get_memory(&mut store, "memory").unwrap(); 634 let mem = memory.data_mut(&mut store); 635 assert!(mem.starts_with(b"1234")); 636 mem[..6].copy_from_slice(b"567890"); 637 638 Ok(()) 639 } 640 641 #[test] 642 #[cfg(target_pointer_width = "64")] 643 #[cfg_attr(miri, ignore)] 644 fn instance_too_large() -> Result<()> { 645 let mut pool = crate::small_pool_config(); 646 pool.max_core_instance_size(16); 647 let mut config = Config::new(); 648 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 649 650 let engine = Engine::new(&config)?; 651 let expected = if cfg!(feature = "wmemcheck") { 652 "\ 653 instance allocation for this module requires 336 bytes which exceeds the \ 654 configured maximum of 16 bytes; breakdown of allocation requirement: 655 656 * 76.19% - 256 bytes - instance state management 657 " 658 } else { 659 "\ 660 instance allocation for this module requires 256 bytes which exceeds the \ 661 configured maximum of 16 bytes; breakdown of allocation requirement: 662 663 * 62.50% - 160 bytes - instance state management 664 * 6.25% - 16 bytes - jit store state 665 " 666 }; 667 match Module::new(&engine, "(module)") { 668 Ok(_) => panic!("should have failed to compile"), 669 Err(e) => assert_eq!(e.to_string(), expected), 670 } 671 672 let mut lots_of_globals = format!("(module"); 673 for _ in 0..100 { 674 lots_of_globals.push_str("(global i32 i32.const 0)\n"); 675 } 676 lots_of_globals.push_str(")"); 677 678 let expected = if cfg!(feature = "wmemcheck") { 679 "\ 680 instance allocation for this module requires 1936 bytes which exceeds the \ 681 configured maximum of 16 bytes; breakdown of allocation requirement: 682 683 * 13.22% - 256 bytes - instance state management 684 * 82.64% - 1600 bytes - defined globals 685 " 686 } else { 687 "\ 688 instance allocation for this module requires 1856 bytes which exceeds the \ 689 configured maximum of 16 bytes; breakdown of allocation requirement: 690 691 * 8.62% - 160 bytes - instance state management 692 * 86.21% - 1600 bytes - defined globals 693 " 694 }; 695 match Module::new(&engine, &lots_of_globals) { 696 Ok(_) => panic!("should have failed to compile"), 697 Err(e) => assert_eq!(e.to_string(), expected), 698 } 699 700 Ok(()) 701 } 702 703 #[test] 704 #[cfg_attr(miri, ignore)] 705 fn dynamic_memory_pooling_allocator() -> Result<()> { 706 for guard_size in [0, 1 << 16] { 707 let max_size = 128 << 20; 708 let mut pool = crate::small_pool_config(); 709 pool.memory_pages(max_size / (64 * 1024)); 710 let mut config = Config::new(); 711 config.static_memory_maximum_size(max_size); 712 config.dynamic_memory_guard_size(guard_size); 713 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 714 715 let engine = Engine::new(&config)?; 716 717 let module = Module::new( 718 &engine, 719 r#" 720 (module 721 (memory (export "memory") 1) 722 723 (func (export "grow") (param i32) (result i32) 724 local.get 0 725 memory.grow) 726 727 (func (export "size") (result i32) 728 memory.size) 729 730 (func (export "i32.load") (param i32) (result i32) 731 local.get 0 732 i32.load) 733 734 (func (export "i32.store") (param i32 i32) 735 local.get 0 736 local.get 1 737 i32.store) 738 739 (data (i32.const 100) "x") 740 ) 741 "#, 742 )?; 743 744 let mut store = Store::new(&engine, ()); 745 let instance = Instance::new(&mut store, &module, &[])?; 746 747 let grow = instance.get_typed_func::<u32, i32>(&mut store, "grow")?; 748 let size = instance.get_typed_func::<(), u32>(&mut store, "size")?; 749 let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?; 750 let i32_store = instance.get_typed_func::<(u32, i32), ()>(&mut store, "i32.store")?; 751 let memory = instance.get_memory(&mut store, "memory").unwrap(); 752 753 // basic length 1 tests 754 // assert_eq!(memory.grow(&mut store, 1)?, 0); 755 assert_eq!(memory.size(&store), 1); 756 assert_eq!(size.call(&mut store, ())?, 1); 757 assert_eq!(i32_load.call(&mut store, 0)?, 0); 758 assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'x')); 759 i32_store.call(&mut store, (0, 0))?; 760 i32_store.call(&mut store, (100, i32::from(b'y')))?; 761 assert_eq!(i32_load.call(&mut store, 100)?, i32::from(b'y')); 762 763 // basic length 2 tests 764 let page = 64 * 1024; 765 assert_eq!(grow.call(&mut store, 1)?, 1); 766 assert_eq!(memory.size(&store), 2); 767 assert_eq!(size.call(&mut store, ())?, 2); 768 i32_store.call(&mut store, (page, 200))?; 769 assert_eq!(i32_load.call(&mut store, page)?, 200); 770 771 // test writes are visible 772 i32_store.call(&mut store, (2, 100))?; 773 assert_eq!(i32_load.call(&mut store, 2)?, 100); 774 775 // test growth can't exceed maximum 776 let too_many = max_size / (64 * 1024); 777 assert_eq!(grow.call(&mut store, too_many as u32)?, -1); 778 assert!(memory.grow(&mut store, too_many).is_err()); 779 780 assert_eq!(memory.data(&store)[page as usize], 200); 781 782 // Re-instantiate in another store. 783 store = Store::new(&engine, ()); 784 let instance = Instance::new(&mut store, &module, &[])?; 785 let i32_load = instance.get_typed_func::<u32, i32>(&mut store, "i32.load")?; 786 let memory = instance.get_memory(&mut store, "memory").unwrap(); 787 788 // This is out of bounds... 789 assert!(i32_load.call(&mut store, page).is_err()); 790 assert_eq!(memory.data_size(&store), page as usize); 791 792 // ... but implementation-wise it should still be mapped memory from 793 // before if we don't have any guard pages. 794 // 795 // Note though that prior writes should all appear as zeros and we can't see 796 // data from the prior instance. 797 // 798 // Note that this part is only implemented on Linux which has 799 // `MADV_DONTNEED`. 800 if cfg!(target_os = "linux") && guard_size == 0 { 801 unsafe { 802 let ptr = memory.data_ptr(&store); 803 assert_eq!(*ptr.offset(page as isize), 0); 804 } 805 } 806 } 807 808 Ok(()) 809 } 810 811 #[test] 812 #[cfg_attr(miri, ignore)] 813 fn zero_memory_pages_disallows_oob() -> Result<()> { 814 let mut pool = crate::small_pool_config(); 815 pool.memory_pages(0); 816 let mut config = Config::new(); 817 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 818 819 let engine = Engine::new(&config)?; 820 let module = Module::new( 821 &engine, 822 r#" 823 (module 824 (memory 0) 825 826 (func (export "load") (param i32) (result i32) 827 local.get 0 828 i32.load) 829 830 (func (export "store") (param i32 ) 831 local.get 0 832 local.get 0 833 i32.store) 834 ) 835 "#, 836 )?; 837 let mut store = Store::new(&engine, ()); 838 let instance = Instance::new(&mut store, &module, &[])?; 839 let load32 = instance.get_typed_func::<i32, i32>(&mut store, "load")?; 840 let store32 = instance.get_typed_func::<i32, ()>(&mut store, "store")?; 841 for i in 0..31 { 842 assert!(load32.call(&mut store, 1 << i).is_err()); 843 assert!(store32.call(&mut store, 1 << i).is_err()); 844 } 845 Ok(()) 846 } 847 848 #[test] 849 #[cfg(feature = "component-model")] 850 fn total_component_instances_limit() -> Result<()> { 851 const TOTAL_COMPONENT_INSTANCES: u32 = 5; 852 853 let mut pool = crate::small_pool_config(); 854 pool.total_component_instances(TOTAL_COMPONENT_INSTANCES); 855 let mut config = Config::new(); 856 config.wasm_component_model(true); 857 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 858 859 let engine = Engine::new(&config)?; 860 let linker = wasmtime::component::Linker::new(&engine); 861 let component = wasmtime::component::Component::new(&engine, "(component)")?; 862 863 let mut store = Store::new(&engine, ()); 864 for _ in 0..TOTAL_COMPONENT_INSTANCES { 865 linker.instantiate(&mut store, &component)?; 866 } 867 868 match linker.instantiate(&mut store, &component) { 869 Ok(_) => panic!("should have hit component instance limit"), 870 Err(e) => assert_eq!( 871 e.to_string(), 872 format!( 873 "maximum concurrent component instance limit of {} reached", 874 TOTAL_COMPONENT_INSTANCES 875 ), 876 ), 877 } 878 879 drop(store); 880 let mut store = Store::new(&engine, ()); 881 for _ in 0..TOTAL_COMPONENT_INSTANCES { 882 linker.instantiate(&mut store, &component)?; 883 } 884 885 Ok(()) 886 } 887 888 #[test] 889 #[cfg(feature = "component-model")] 890 fn component_instance_size_limit() -> Result<()> { 891 let mut pool = crate::small_pool_config(); 892 pool.max_component_instance_size(1); 893 let mut config = Config::new(); 894 config.wasm_component_model(true); 895 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 896 let engine = Engine::new(&config)?; 897 898 match wasmtime::component::Component::new(&engine, "(component)") { 899 Ok(_) => panic!("should have hit limit"), 900 Err(e) => assert_eq!( 901 e.to_string(), 902 "instance allocation for this component requires 64 bytes of `VMComponentContext` space \ 903 which exceeds the configured maximum of 1 bytes" 904 ), 905 } 906 907 Ok(()) 908 } 909 910 #[test] 911 #[cfg_attr(miri, ignore)] 912 fn total_tables_limit() -> Result<()> { 913 const TOTAL_TABLES: u32 = 5; 914 915 let mut pool = crate::small_pool_config(); 916 pool.total_tables(TOTAL_TABLES) 917 .total_core_instances(TOTAL_TABLES + 1); 918 let mut config = Config::new(); 919 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 920 921 let engine = Engine::new(&config)?; 922 let linker = Linker::new(&engine); 923 let module = Module::new(&engine, "(module (table 0 1 funcref))")?; 924 925 let mut store = Store::new(&engine, ()); 926 for _ in 0..TOTAL_TABLES { 927 linker.instantiate(&mut store, &module)?; 928 } 929 930 match linker.instantiate(&mut store, &module) { 931 Ok(_) => panic!("should have hit table limit"), 932 Err(e) => assert_eq!( 933 e.to_string(), 934 format!("maximum concurrent table limit of {} reached", TOTAL_TABLES), 935 ), 936 } 937 938 drop(store); 939 let mut store = Store::new(&engine, ()); 940 for _ in 0..TOTAL_TABLES { 941 linker.instantiate(&mut store, &module)?; 942 } 943 944 Ok(()) 945 } 946 947 #[tokio::test] 948 #[cfg(not(miri))] 949 async fn total_stacks_limit() -> Result<()> { 950 use super::async_functions::PollOnce; 951 952 const TOTAL_STACKS: u32 = 2; 953 954 let mut pool = crate::small_pool_config(); 955 pool.total_stacks(TOTAL_STACKS) 956 .total_core_instances(TOTAL_STACKS + 1); 957 let mut config = Config::new(); 958 config.async_support(true); 959 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 960 961 let engine = Engine::new(&config)?; 962 963 let mut linker = Linker::new(&engine); 964 linker.func_new_async( 965 "async", 966 "yield", 967 FuncType::new(&engine, [], []), 968 |_caller, _params, _results| { 969 Box::new(async { 970 tokio::task::yield_now().await; 971 Ok(()) 972 }) 973 }, 974 )?; 975 976 let module = Module::new( 977 &engine, 978 r#" 979 (module 980 (import "async" "yield" (func $yield)) 981 (func (export "run") 982 call $yield 983 ) 984 ) 985 "#, 986 )?; 987 988 // Allocate stacks up to the limit. (Poll the futures once to make sure we 989 // actually enter Wasm and force a stack allocation.) 990 991 let mut store1 = Store::new(&engine, ()); 992 let instance1 = linker.instantiate_async(&mut store1, &module).await?; 993 let run1 = instance1.get_func(&mut store1, "run").unwrap(); 994 let future1 = PollOnce::new(Box::pin(run1.call_async(store1, &[], &mut []))) 995 .await 996 .unwrap_err(); 997 998 let mut store2 = Store::new(&engine, ()); 999 let instance2 = linker.instantiate_async(&mut store2, &module).await?; 1000 let run2 = instance2.get_func(&mut store2, "run").unwrap(); 1001 let future2 = PollOnce::new(Box::pin(run2.call_async(store2, &[], &mut []))) 1002 .await 1003 .unwrap_err(); 1004 1005 // Allocating more should fail. 1006 let mut store3 = Store::new(&engine, ()); 1007 match linker.instantiate_async(&mut store3, &module).await { 1008 Ok(_) => panic!("should have hit stack limit"), 1009 Err(e) => assert_eq!( 1010 e.to_string(), 1011 format!("maximum concurrent fiber limit of {} reached", TOTAL_STACKS), 1012 ), 1013 } 1014 1015 // Finish the futures and return their Wasm stacks to the pool. 1016 future1.await?; 1017 future2.await?; 1018 1019 // Should be able to allocate new stacks again. 1020 let mut store1 = Store::new(&engine, ()); 1021 let instance1 = linker.instantiate_async(&mut store1, &module).await?; 1022 let run1 = instance1.get_func(&mut store1, "run").unwrap(); 1023 let future1 = run1.call_async(store1, &[], &mut []); 1024 1025 let mut store2 = Store::new(&engine, ()); 1026 let instance2 = linker.instantiate_async(&mut store2, &module).await?; 1027 let run2 = instance2.get_func(&mut store2, "run").unwrap(); 1028 let future2 = run2.call_async(store2, &[], &mut []); 1029 1030 future1.await?; 1031 future2.await?; 1032 1033 Ok(()) 1034 } 1035 1036 #[test] 1037 #[cfg(feature = "component-model")] 1038 fn component_core_instances_limit() -> Result<()> { 1039 let mut pool = crate::small_pool_config(); 1040 pool.max_core_instances_per_component(1); 1041 let mut config = Config::new(); 1042 config.wasm_component_model(true); 1043 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 1044 let engine = Engine::new(&config)?; 1045 1046 // One core instance works. 1047 wasmtime::component::Component::new( 1048 &engine, 1049 r#" 1050 (component 1051 (core module $m) 1052 (core instance $a (instantiate $m)) 1053 ) 1054 "#, 1055 )?; 1056 1057 // Two core instances doesn't. 1058 match wasmtime::component::Component::new( 1059 &engine, 1060 r#" 1061 (component 1062 (core module $m) 1063 (core instance $a (instantiate $m)) 1064 (core instance $b (instantiate $m)) 1065 ) 1066 "#, 1067 ) { 1068 Ok(_) => panic!("should have hit limit"), 1069 Err(e) => assert_eq!( 1070 e.to_string(), 1071 "The component transitively contains 2 core module instances, which exceeds the \ 1072 configured maximum of 1" 1073 ), 1074 } 1075 1076 Ok(()) 1077 } 1078 1079 #[test] 1080 #[cfg(feature = "component-model")] 1081 fn component_memories_limit() -> Result<()> { 1082 let mut pool = crate::small_pool_config(); 1083 pool.max_memories_per_component(1).total_memories(2); 1084 let mut config = Config::new(); 1085 config.wasm_component_model(true); 1086 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 1087 let engine = Engine::new(&config)?; 1088 1089 // One memory works. 1090 wasmtime::component::Component::new( 1091 &engine, 1092 r#" 1093 (component 1094 (core module $m (memory 1 1)) 1095 (core instance $a (instantiate $m)) 1096 ) 1097 "#, 1098 )?; 1099 1100 // Two memories doesn't. 1101 match wasmtime::component::Component::new( 1102 &engine, 1103 r#" 1104 (component 1105 (core module $m (memory 1 1)) 1106 (core instance $a (instantiate $m)) 1107 (core instance $b (instantiate $m)) 1108 ) 1109 "#, 1110 ) { 1111 Ok(_) => panic!("should have hit limit"), 1112 Err(e) => assert_eq!( 1113 e.to_string(), 1114 "The component transitively contains 2 Wasm linear memories, which exceeds the \ 1115 configured maximum of 1" 1116 ), 1117 } 1118 1119 Ok(()) 1120 } 1121 1122 #[test] 1123 #[cfg(feature = "component-model")] 1124 fn component_tables_limit() -> Result<()> { 1125 let mut pool = crate::small_pool_config(); 1126 pool.max_tables_per_component(1).total_tables(2); 1127 let mut config = Config::new(); 1128 config.wasm_component_model(true); 1129 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 1130 let engine = Engine::new(&config)?; 1131 1132 // One table works. 1133 wasmtime::component::Component::new( 1134 &engine, 1135 r#" 1136 (component 1137 (core module $m (table 1 1 funcref)) 1138 (core instance $a (instantiate $m)) 1139 ) 1140 "#, 1141 )?; 1142 1143 // Two tables doesn't. 1144 match wasmtime::component::Component::new( 1145 &engine, 1146 r#" 1147 (component 1148 (core module $m (table 1 1 funcref)) 1149 (core instance $a (instantiate $m)) 1150 (core instance $b (instantiate $m)) 1151 ) 1152 "#, 1153 ) { 1154 Ok(_) => panic!("should have hit limit"), 1155 Err(e) => assert_eq!( 1156 e.to_string(), 1157 "The component transitively contains 2 tables, which exceeds the \ 1158 configured maximum of 1" 1159 ), 1160 } 1161 1162 Ok(()) 1163 } 1164 1165 #[test] 1166 #[cfg_attr(miri, ignore)] 1167 fn total_memories_limit() -> Result<()> { 1168 const TOTAL_MEMORIES: u32 = 5; 1169 1170 let mut pool = crate::small_pool_config(); 1171 pool.total_memories(TOTAL_MEMORIES) 1172 .total_core_instances(TOTAL_MEMORIES + 1) 1173 .memory_protection_keys(MpkEnabled::Disable); 1174 let mut config = Config::new(); 1175 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); 1176 1177 let engine = Engine::new(&config)?; 1178 let linker = Linker::new(&engine); 1179 let module = Module::new(&engine, "(module (memory 1 1))")?; 1180 1181 let mut store = Store::new(&engine, ()); 1182 for _ in 0..TOTAL_MEMORIES { 1183 linker.instantiate(&mut store, &module)?; 1184 } 1185 1186 match linker.instantiate(&mut store, &module) { 1187 Ok(_) => panic!("should have hit memory limit"), 1188 Err(e) => assert_eq!( 1189 e.to_string(), 1190 format!( 1191 "maximum concurrent memory limit of {} reached for stripe 0", 1192 TOTAL_MEMORIES 1193 ), 1194 ), 1195 } 1196 1197 drop(store); 1198 let mut store = Store::new(&engine, ()); 1199 for _ in 0..TOTAL_MEMORIES { 1200 linker.instantiate(&mut store, &module)?; 1201 } 1202 1203 Ok(()) 1204 } 1205