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]
checks_incompatible_target() -> Result<()>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]
caches_across_engines()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)]
aot_compiles() -> Result<()>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)]
serialize_deterministic()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]
serialize_not_overly_massive() -> Result<()>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)]
missing_sse_and_floats_still_works() -> Result<()>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)]
large_add_chain_no_stack_overflow() -> Result<()>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]
compile_a_component() -> Result<()>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)]
tail_call_defaults() -> Result<()>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)]
cross_engine_module_exports() -> Result<()>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)]
concurrent_type_registry_modifications(config: &mut Config) -> Result<()>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)]
concurrent_type_modifications_and_checks(config: &mut Config) -> Result<()>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)]
validate_deterministic()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)]
deserialize_raw_avoids_copy()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)]
deserialize_raw_fails_for_native()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