xref: /wasmtime-44.0.1/tests/all/module.rs (revision 94740588)
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