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