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