1 use std::sync::Arc; 2 use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; 3 use target_lexicon::Triple; 4 use wasmtime::error::Context as _; 5 use wasmtime::*; 6 use wasmtime_environ::TripleExt; 7 use wasmtime_test_macros::wasmtime_test; 8 9 #[test] 10 fn checks_incompatible_target() -> Result<()> { 11 // For platforms that Cranelift supports make sure a mismatch generates an 12 // error 13 if cfg!(target_arch = "x86_64") 14 || cfg!(target_arch = "aarch64") 15 || cfg!(target_arch = "s390x") 16 || cfg!(target_arch = "riscv64") 17 { 18 let mut target = target_lexicon::Triple::host(); 19 target.operating_system = target_lexicon::OperatingSystem::Unknown; 20 assert_invalid_target(&target.to_string())?; 21 } 22 23 // Otherwise make sure that the wrong pulley target is rejected on all 24 // platforms. 25 let wrong_pulley = if cfg!(target_pointer_width = "32") { 26 "pulley64" 27 } else { 28 "pulley32" 29 }; 30 assert_invalid_target(wrong_pulley)?; 31 32 return Ok(()); 33 34 fn assert_invalid_target(target: &str) -> Result<()> { 35 match Module::new(&Engine::new(Config::new().target(target)?)?, "(module)") { 36 Ok(_) => unreachable!(), 37 Err(e) => assert!( 38 format!("{e:?}").contains("configuration does not match the host"), 39 "bad error: {e:?}" 40 ), 41 } 42 43 Ok(()) 44 } 45 } 46 47 #[test] 48 fn caches_across_engines() { 49 let c = Config::new(); 50 51 let bytes = Module::new(&Engine::new(&c).unwrap(), "(module)") 52 .unwrap() 53 .serialize() 54 .unwrap(); 55 56 unsafe { 57 let res = Module::deserialize(&Engine::default(), &bytes); 58 assert!(res.is_ok()); 59 60 // differ in runtime settings 61 let res = Module::deserialize( 62 &Engine::new(Config::new().memory_reservation(0)).unwrap(), 63 &bytes, 64 ); 65 assert!(res.is_err()); 66 67 // differ in wasm features enabled (which can affect 68 // runtime/compilation settings) 69 let res = Module::deserialize( 70 &Engine::new(Config::new().wasm_relaxed_simd(false)).unwrap(), 71 &bytes, 72 ); 73 assert!(res.is_err()); 74 } 75 } 76 77 #[test] 78 #[cfg_attr(miri, ignore)] 79 fn aot_compiles() -> Result<()> { 80 let engine = Engine::default(); 81 let bytes = engine.precompile_module( 82 "(module (func (export \"f\") (param i32) (result i32) local.get 0))".as_bytes(), 83 )?; 84 85 let module = unsafe { Module::deserialize(&engine, &bytes)? }; 86 87 let mut store = Store::new(&engine, ()); 88 let instance = Instance::new(&mut store, &module, &[])?; 89 90 let f = instance.get_typed_func::<i32, i32>(&mut store, "f")?; 91 assert_eq!(f.call(&mut store, 101)?, 101); 92 93 Ok(()) 94 } 95 96 #[test] 97 #[cfg_attr(miri, ignore)] 98 fn serialize_deterministic() { 99 let engine = Engine::default(); 100 101 let assert_deterministic = |wasm: &str| { 102 let p1 = engine.precompile_module(wasm.as_bytes()).unwrap(); 103 let p2 = engine.precompile_module(wasm.as_bytes()).unwrap(); 104 if p1 != p2 { 105 panic!("precompile_module not deterministic for:\n{wasm}"); 106 } 107 108 let module1 = Module::new(&engine, wasm).unwrap(); 109 let a1 = module1.serialize().unwrap(); 110 let a2 = module1.serialize().unwrap(); 111 if a1 != a2 { 112 panic!("Module::serialize not deterministic for:\n{wasm}"); 113 } 114 115 let module2 = Module::new(&engine, wasm).unwrap(); 116 let b1 = module2.serialize().unwrap(); 117 let b2 = module2.serialize().unwrap(); 118 if b1 != b2 { 119 panic!("Module::serialize not deterministic for:\n{wasm}"); 120 } 121 122 if a1 != b2 { 123 panic!("not matching across modules:\n{wasm}"); 124 } 125 if b1 != p2 { 126 panic!("not matching across engine/module:\n{wasm}"); 127 } 128 }; 129 130 assert_deterministic("(module)"); 131 assert_deterministic("(module (func))"); 132 assert_deterministic("(module (func nop))"); 133 assert_deterministic("(module (func) (func (param i32)))"); 134 assert_deterministic("(module (func (export \"f\")) (func (export \"y\")))"); 135 assert_deterministic("(module (func $f) (func $g))"); 136 assert_deterministic("(module (data \"\") (data \"\"))"); 137 assert_deterministic("(module (elem func) (elem func))"); 138 } 139 140 // This test asserts that the optimization to transform separate data segments 141 // into an initialization image doesn't unnecessarily create a massive module by 142 // accident with a very large initialization image in it. 143 #[test] 144 fn serialize_not_overly_massive() -> Result<()> { 145 let mut config = Config::new(); 146 config.memory_guaranteed_dense_image_size(1 << 20); 147 let engine = Engine::new(&config)?; 148 149 let assert_smaller_than_1mb = |module: &str| -> Result<()> { 150 println!("{module}"); 151 let bytes = Module::new(&engine, module)?.serialize()?; 152 assert!(bytes.len() < (1 << 20)); 153 Ok(()) 154 }; 155 156 // Tons of space between data segments should use sparse initialization, 157 // along with various permutations of empty and nonempty segments. 158 assert_smaller_than_1mb( 159 r#"(module 160 (memory 20000) 161 (data (i32.const 0) "a") 162 (data (i32.const 0x200000) "b") 163 )"#, 164 )?; 165 assert_smaller_than_1mb( 166 r#"(module 167 (memory 20000) 168 (data (i32.const 0) "a") 169 (data (i32.const 0x200000) "") 170 )"#, 171 )?; 172 assert_smaller_than_1mb( 173 r#"(module 174 (memory 20000) 175 (data (i32.const 0) "") 176 (data (i32.const 0x200000) "b") 177 )"#, 178 )?; 179 assert_smaller_than_1mb( 180 r#"(module 181 (memory 20000) 182 (data (i32.const 0) "") 183 (data (i32.const 0x200000) "") 184 )"#, 185 )?; 186 187 // lone data segment 188 assert_smaller_than_1mb( 189 r#"(module 190 (memory 20000) 191 (data (i32.const 0x200000) "b") 192 )"#, 193 )?; 194 195 Ok(()) 196 } 197 198 // This test specifically disables SSE4.1 in Cranelift which force wasm 199 // instructions like `f32.ceil` to go through libcalls instead of using native 200 // instructions. Note that SIMD is also disabled here because SIMD otherwise 201 // requires SSE4.1 to be enabled. 202 // 203 // This test then also tests that loading modules through various means, e.g. 204 // through precompiled artifacts, all works. 205 #[test] 206 #[cfg_attr(any(not(target_arch = "x86_64"), miri), ignore)] 207 fn missing_sse_and_floats_still_works() -> Result<()> { 208 let mut config = Config::new(); 209 config.wasm_simd(false).wasm_relaxed_simd(false); 210 unsafe { 211 config.cranelift_flag_set("has_sse41", "false"); 212 } 213 let engine = Engine::new(&config)?; 214 let module = Module::new( 215 &engine, 216 r#" 217 (module 218 (func (export "f32.ceil") (param f32) (result f32) 219 local.get 0 220 f32.ceil) 221 ) 222 "#, 223 )?; 224 let bytes = module.serialize()?; 225 let module2 = unsafe { Module::deserialize(&engine, &bytes)? }; 226 let tmpdir = tempfile::TempDir::new()?; 227 let path = tmpdir.path().join("module.cwasm"); 228 std::fs::write(&path, &bytes)?; 229 let module3 = unsafe { Module::deserialize_file(&engine, &path)? }; 230 231 for module in [module, module2, module3] { 232 let mut store = Store::new(&engine, ()); 233 let instance = Instance::new(&mut store, &module, &[])?; 234 let ceil = instance.get_typed_func::<f32, f32>(&mut store, "f32.ceil")?; 235 236 for f in [1.0, 2.3, -1.3] { 237 assert_eq!(ceil.call(&mut store, f)?, f.ceil()); 238 } 239 } 240 241 Ok(()) 242 } 243 244 #[test] 245 #[cfg_attr(miri, ignore)] 246 fn large_add_chain_no_stack_overflow() -> Result<()> { 247 let mut config = Config::new(); 248 config.cranelift_opt_level(OptLevel::None); 249 let engine = Engine::new(&config)?; 250 let mut wat = String::from( 251 " 252 (module 253 (func (result i64) 254 (i64.const 1) 255 ", 256 ); 257 for _ in 0..20_000 { 258 wat.push_str("(i64.add (i64.const 1))\n"); 259 } 260 261 wat.push_str(")\n)"); 262 Module::new(&engine, &wat)?; 263 264 Ok(()) 265 } 266 267 #[test] 268 fn compile_a_component() -> Result<()> { 269 let engine = Engine::default(); 270 let err = Module::new(&engine, "(component)").unwrap_err(); 271 let err = format!("{err:?}"); 272 assert!( 273 err.contains("expected a WebAssembly module but was given a WebAssembly component"), 274 "bad error: {err}" 275 ); 276 Ok(()) 277 } 278 279 #[test] 280 #[cfg_attr(miri, ignore)] 281 fn tail_call_defaults() -> Result<()> { 282 let wasm_with_tail_calls = "(module (func $a return_call $a))"; 283 284 // on by default 285 Module::new(&Engine::default(), wasm_with_tail_calls)?; 286 287 // on by default for cranelift 288 Module::new( 289 &Engine::new(Config::new().strategy(Strategy::Cranelift))?, 290 wasm_with_tail_calls, 291 )?; 292 293 if cfg!(target_arch = "x86_64") { 294 // off by default for winch 295 let err = Module::new( 296 &Engine::new(Config::new().strategy(Strategy::Winch))?, 297 wasm_with_tail_calls, 298 ); 299 assert!(err.is_err()); 300 } 301 Ok(()) 302 } 303 304 #[test] 305 #[cfg_attr(miri, ignore)] 306 fn cross_engine_module_exports() -> Result<()> { 307 let a_engine = Engine::default(); 308 let b_engine = Engine::default(); 309 310 let a_module = Module::new(&a_engine, "(module)")?; 311 let b_module = Module::new( 312 &b_engine, 313 r#" 314 (module 315 (func (export "x")) 316 ) 317 "#, 318 )?; 319 320 let export = b_module.get_export_index("x").unwrap(); 321 322 let mut store = Store::new(&a_engine, ()); 323 let instance = Instance::new(&mut store, &a_module, &[])?; 324 assert!(instance.get_module_export(&mut store, &export).is_none()); 325 Ok(()) 326 } 327 328 /// Smoke test for registering and unregistering modules (and their rec group 329 /// entries) concurrently. 330 #[wasmtime_test(wasm_features(gc, function_references))] 331 #[cfg_attr(miri, ignore)] 332 fn concurrent_type_registry_modifications(config: &mut Config) -> Result<()> { 333 let _ = env_logger::try_init(); 334 335 // The number of seconds to run the smoke test. 336 const TEST_DURATION_SECONDS: u64 = 5; 337 338 // The number of worker threads to spawn for this smoke test. 339 const NUM_WORKER_THREADS: usize = 32; 340 341 let engine = Engine::new(config)?; 342 343 /// Tests of various kinds of modifications to the type registry. 344 enum Test { 345 /// Creating a module (from its text format) should register new entries 346 /// in the type registry. 347 Module(&'static str), 348 /// Creating an individual func type registers a singleton entry in the 349 /// registry which is managed slightly differently from modules. 350 Func(fn(&Engine) -> FuncType), 351 /// Create a single struct type like a single function type. 352 Struct(fn(&Engine) -> StructType), 353 /// Create a single array type like a single function type. 354 Array(fn(&Engine) -> ArrayType), 355 } 356 const TESTS: &'static [Test] = &[ 357 Test::Func(|engine| FuncType::new(engine, [], [])), 358 Test::Func(|engine| FuncType::new(engine, [], [ValType::I32])), 359 Test::Func(|engine| FuncType::new(engine, [ValType::I32], [])), 360 Test::Struct(|engine| StructType::new(engine, []).unwrap()), 361 Test::Array(|engine| { 362 ArrayType::new(engine, FieldType::new(Mutability::Const, StorageType::I8)) 363 }), 364 Test::Array(|engine| { 365 ArrayType::new(engine, FieldType::new(Mutability::Var, StorageType::I8)) 366 }), 367 Test::Module( 368 r#" 369 (module 370 ;; A handful of function types. 371 (type (func)) 372 (type (func (param i32))) 373 (type (func (result i32))) 374 (type (func (param i32) (result i32))) 375 376 ;; A handful of recursive types. 377 (rec) 378 (rec (type $s (struct (field (ref null $s))))) 379 (rec (type $a (struct (field (ref null $b)))) 380 (type $b (struct (field (ref null $a))))) 381 (rec (type $c (struct (field (ref null $b)) 382 (field (ref null $d)))) 383 (type $d (struct (field (ref null $a)) 384 (field (ref null $c))))) 385 386 ;; Some GC types 387 (type (struct)) 388 (type (array i8)) 389 (type (array (mut i8))) 390 ) 391 "#, 392 ), 393 Test::Module( 394 r#" 395 (module 396 ;; Just the function types. 397 (type (func)) 398 (type (func (param i32))) 399 (type (func (result i32))) 400 (type (func (param i32) (result i32))) 401 ) 402 "#, 403 ), 404 Test::Module( 405 r#" 406 (module 407 ;; Just the recursive types. 408 (rec) 409 (rec (type $s (struct (field (ref null $s))))) 410 (rec (type $a (struct (field (ref null $b)))) 411 (type $b (struct (field (ref null $a))))) 412 (rec (type $c (struct (field (ref null $b)) 413 (field (ref null $d)))) 414 (type $d (struct (field (ref null $a)) 415 (field (ref null $c))))) 416 ) 417 "#, 418 ), 419 Test::Module( 420 r#" 421 (module 422 ;; One of each kind of type. 423 (type (func (param i32) (result i32))) 424 (rec (type $a (struct (field (ref null $b)))) 425 (type $b (struct (field (ref null $a))))) 426 ) 427 "#, 428 ), 429 ]; 430 431 // Spawn the worker threads, each of them just registering and unregistering 432 // modules (and their types) constantly for the duration of the smoke test. 433 let handles = (0..NUM_WORKER_THREADS) 434 .map(|_| { 435 let engine = engine.clone(); 436 std::thread::spawn(move || -> Result<()> { 437 let mut tests = TESTS.iter().cycle(); 438 let start = std::time::Instant::now(); 439 while start.elapsed().as_secs() < TEST_DURATION_SECONDS { 440 match tests.next() { 441 Some(Test::Module(wat)) => { 442 let _ = Module::new(&engine, wat)?; 443 } 444 Some(Test::Func(ctor)) => { 445 let _ = ctor(&engine); 446 } 447 Some(Test::Struct(ctor)) => { 448 let _ = ctor(&engine); 449 } 450 Some(Test::Array(ctor)) => { 451 let _ = ctor(&engine); 452 } 453 None => unreachable!(), 454 } 455 } 456 457 Ok(()) 458 }) 459 }) 460 .collect::<Vec<_>>(); 461 462 // Join all of the thread handles. 463 for handle in handles { 464 handle 465 .join() 466 .expect("should join thread handle") 467 .context("error during thread execution")?; 468 } 469 470 Ok(()) 471 } 472 473 #[wasmtime_test(wasm_features(function_references))] 474 #[cfg_attr(miri, ignore)] 475 fn concurrent_type_modifications_and_checks(config: &mut Config) -> Result<()> { 476 const THREADS_CHECKING: usize = 4; 477 478 let _ = env_logger::try_init(); 479 480 let engine = Engine::new(&config)?; 481 482 let mut threads = Vec::new(); 483 let keep_going = Arc::new(AtomicBool::new(true)); 484 485 // Spawn a number of threads that are all working with a module and testing 486 // various properties about type-checks in the module. 487 for _ in 0..THREADS_CHECKING { 488 threads.push(std::thread::spawn({ 489 let engine = engine.clone(); 490 let keep_going = keep_going.clone(); 491 move || -> Result<()> { 492 while keep_going.load(Relaxed) { 493 let module = Module::new( 494 &engine, 495 r#" 496 (module 497 (func (export "f") (param funcref) 498 i32.const 0 499 local.get 0 500 table.set 501 i32.const 0 502 call_indirect (result f64) 503 drop 504 ) 505 506 (table 1 funcref) 507 ) 508 "#, 509 )?; 510 let ty = FuncType::new(&engine, [], [ValType::I32]); 511 let mut store = Store::new(&engine, ()); 512 let func = Func::new(&mut store, ty, |_, _, results| { 513 results[0] = Val::I32(0); 514 Ok(()) 515 }); 516 517 let instance = Instance::new(&mut store, &module, &[])?; 518 assert!(instance.get_typed_func::<(), i32>(&mut store, "f").is_err()); 519 assert!(instance.get_typed_func::<(), f64>(&mut store, "f").is_err()); 520 let f = instance.get_typed_func::<Func, ()>(&mut store, "f")?; 521 let err = f.call(&mut store, func).unwrap_err(); 522 assert_eq!(err.downcast::<Trap>()?, Trap::BadSignature); 523 } 524 Ok(()) 525 } 526 })); 527 } 528 529 // Spawn threads in the background creating/destroying `FuncType`s related 530 // to the module above. 531 threads.push(std::thread::spawn({ 532 let engine = engine.clone(); 533 let keep_going = keep_going.clone(); 534 move || -> Result<()> { 535 while keep_going.load(Relaxed) { 536 FuncType::new(&engine, [], [ValType::F64]); 537 } 538 Ok(()) 539 } 540 })); 541 threads.push(std::thread::spawn({ 542 let engine = engine.clone(); 543 let keep_going = keep_going.clone(); 544 move || -> Result<()> { 545 while keep_going.load(Relaxed) { 546 FuncType::new(&engine, [], [ValType::I32]); 547 } 548 Ok(()) 549 } 550 })); 551 552 std::thread::sleep(std::time::Duration::new(2, 0)); 553 keep_going.store(false, Relaxed); 554 555 for thread in threads { 556 thread.join().unwrap()?; 557 } 558 559 Ok(()) 560 } 561 562 #[test] 563 #[cfg_attr(miri, ignore)] 564 fn validate_deterministic() { 565 let mut faulty_wat = "(module ".to_string(); 566 for i in 0..100 { 567 faulty_wat.push_str(&format!( 568 "(func (export \"foo_{i}\") (result i64) (i64.add (i32.const 0) (i64.const 1)))" 569 )); 570 } 571 faulty_wat.push_str(")"); 572 let binary = wat::parse_str(faulty_wat).unwrap(); 573 574 let engine_parallel = Engine::new(&Config::new().parallel_compilation(true)).unwrap(); 575 let result_parallel = Module::validate(&engine_parallel, &binary) 576 .unwrap_err() 577 .to_string(); 578 579 let engine_sequential = Engine::new(&Config::new().parallel_compilation(false)).unwrap(); 580 let result_sequential = Module::validate(&engine_sequential, &binary) 581 .unwrap_err() 582 .to_string(); 583 assert_eq!(result_parallel, result_sequential); 584 } 585 586 #[test] 587 #[cfg_attr(miri, ignore)] 588 fn deserialize_raw_avoids_copy() { 589 // target pulley; executing code directly requires virtual memory 590 let mut config = Config::new(); 591 let target = format!("{}", Triple::pulley_host()); 592 config.target(&target).unwrap(); 593 let engine = Engine::new(&config).unwrap(); 594 let wat = String::from( 595 r#" 596 (module 597 (func (export "add") (param $lhs i32) (param $rhs i32) (result i32) 598 (i32.add (local.get $lhs) (local.get $rhs)) 599 ) 600 ) 601 "#, 602 ); 603 let module = Module::new(&engine, &wat).unwrap(); 604 let mut serialized = module.serialize().expect("Serialize failed"); 605 let serialized_ptr = std::ptr::slice_from_raw_parts(serialized.as_mut_ptr(), serialized.len()); 606 let module_memory = std::ptr::NonNull::new(serialized_ptr.cast_mut()).unwrap(); 607 let deserialized_module = 608 unsafe { Module::deserialize_raw(&engine, module_memory).expect("Deserialize Failed") }; 609 610 // assert that addresses haven't changed, no full copy 611 // TODO: haven't been able to find a pub way of doing this 612 613 // basic verification that the loaded module works 614 let mut store = Store::new(&engine, ()); 615 let instance = Instance::new(&mut store, &deserialized_module, &[]).unwrap(); 616 let f = instance 617 .get_typed_func::<(i32, i32), i32>(&mut store, "add") 618 .unwrap(); 619 assert_eq!(f.call(&mut store, (26, 50)).unwrap(), 76); 620 } 621 622 #[test] 623 #[cfg_attr(miri, ignore)] 624 fn deserialize_raw_fails_for_native() { 625 // target pulley 626 let engine = Engine::default(); 627 let wat = String::from( 628 r#" 629 (module 630 (func (export "add") (param $lhs i32) (param $rhs i32) (result i32) 631 (i32.add (local.get $lhs) (local.get $rhs)) 632 ) 633 ) 634 "#, 635 ); 636 let module = Module::new(&engine, &wat).unwrap(); 637 let mut serialized = module.serialize().expect("Serialize failed"); 638 let serialized_ptr = std::ptr::slice_from_raw_parts(serialized.as_mut_ptr(), serialized.len()); 639 let module_memory = std::ptr::NonNull::new(serialized_ptr.cast_mut()).unwrap(); 640 let deserialize_res = unsafe { Module::deserialize_raw(&engine, module_memory) }; 641 642 if engine.is_pulley() { 643 let _mod = deserialize_res.expect("Module should deserialize fine for pulley"); 644 } else { 645 let err = deserialize_res.expect_err("Deserialization should fail for host target"); 646 assert_eq!( 647 format!("{err}"), 648 "this target requires virtual memory to be enabled" 649 ); 650 } 651 } 652