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