xref: /wasmtime-44.0.1/tests/all/traps.rs (revision c8764ed0)
1 #![cfg(not(miri))]
2 
3 use crate::ErrorExt;
4 use std::num::NonZeroUsize;
5 use std::panic::{self, AssertUnwindSafe};
6 use std::process::Command;
7 use std::sync::{Arc, Mutex};
8 use wasmtime::bail;
9 use wasmtime::*;
10 use wasmtime_test_macros::wasmtime_test;
11 
12 #[test]
test_trap_return() -> Result<()>13 fn test_trap_return() -> Result<()> {
14     let mut store = Store::<()>::default();
15     let wat = r#"
16         (module
17         (func $hello (import "" "hello"))
18         (func (export "run") (call $hello))
19         )
20     "#;
21 
22     let module = Module::new(store.engine(), wat)?;
23     let hello_type = FuncType::new(store.engine(), None, None);
24     let hello_func = Func::new(&mut store, hello_type, |_, _, _| bail!("test 123"));
25 
26     let instance = Instance::new(&mut store, &module, &[hello_func.into()])?;
27     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
28 
29     let e = run_func.call(&mut store, ()).unwrap_err();
30     e.assert_contains("test 123");
31 
32     assert!(
33         e.downcast_ref::<WasmBacktrace>().is_some(),
34         "error should contain a WasmBacktrace"
35     );
36 
37     Ok(())
38 }
39 
40 #[test]
test_format_err_error_return() -> Result<()>41 fn test_format_err_error_return() -> Result<()> {
42     let mut store = Store::<()>::default();
43     let wat = r#"
44         (module
45         (func $hello (import "" "hello"))
46         (func (export "run") (call $hello))
47         )
48     "#;
49 
50     let module = Module::new(store.engine(), wat)?;
51     let hello_type = FuncType::new(store.engine(), None, None);
52     let hello_func = Func::new(&mut store, hello_type, |_, _, _| {
53         Err(wasmtime::Error::msg("test 1234"))
54     });
55 
56     let instance = Instance::new(&mut store, &module, &[hello_func.into()])?;
57     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
58 
59     let e = run_func.call(&mut store, ()).unwrap_err();
60     e.assert_contains("test 1234");
61 
62     assert!(e.downcast_ref::<Trap>().is_none());
63     assert!(e.downcast_ref::<WasmBacktrace>().is_some());
64 
65     Ok(())
66 }
67 
68 #[test]
test_trap_return_downcast() -> Result<()>69 fn test_trap_return_downcast() -> Result<()> {
70     let mut store = Store::<()>::default();
71     let wat = r#"
72         (module
73         (func $hello (import "" "hello"))
74         (func (export "run") (call $hello))
75         )
76     "#;
77 
78     #[derive(Debug)]
79     struct MyTrap;
80     impl std::fmt::Display for MyTrap {
81         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82             write!(f, "my trap")
83         }
84     }
85     impl std::error::Error for MyTrap {}
86 
87     let module = Module::new(store.engine(), wat)?;
88     let hello_type = FuncType::new(store.engine(), None, None);
89     let hello_func = Func::new(&mut store, hello_type, |_, _, _| {
90         Err(wasmtime::Error::from(MyTrap))
91     });
92 
93     let instance = Instance::new(&mut store, &module, &[hello_func.into()])?;
94     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
95 
96     let e = run_func
97         .call(&mut store, ())
98         .err()
99         .expect("error calling function");
100 
101     assert!(!e.to_string().contains("my trap"));
102     e.assert_contains("my trap");
103 
104     e.downcast_ref::<MyTrap>()
105         .expect("error downcasts to MyTrap");
106     let bt = e
107         .downcast_ref::<WasmBacktrace>()
108         .expect("error downcasts to WasmBacktrace");
109     assert_eq!(bt.frames().len(), 1);
110     println!("{bt:?}");
111 
112     Ok(())
113 }
114 
115 #[test]
test_trap_trace() -> Result<()>116 fn test_trap_trace() -> Result<()> {
117     let mut store = Store::<()>::default();
118     let wat = r#"
119         (module $hello_mod
120             (func (export "run") (call $hello))
121             (func $hello (unreachable))
122         )
123     "#;
124 
125     let module = Module::new(store.engine(), wat)?;
126     let instance = Instance::new(&mut store, &module, &[])?;
127     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
128 
129     let e = run_func.call(&mut store, ()).unwrap_err();
130 
131     let trace = e.downcast_ref::<WasmBacktrace>().unwrap().frames();
132     assert_eq!(trace.len(), 2);
133     assert_eq!(trace[0].module().name().unwrap(), "hello_mod");
134     assert_eq!(trace[0].func_index(), 1);
135     assert_eq!(trace[0].func_name(), Some("hello"));
136     assert_eq!(trace[0].func_offset(), Some(1));
137     assert_eq!(trace[0].module_offset(), Some(0x26));
138     assert_eq!(trace[1].module().name().unwrap(), "hello_mod");
139     assert_eq!(trace[1].func_index(), 0);
140     assert_eq!(trace[1].func_name(), None);
141     assert_eq!(trace[1].func_offset(), Some(1));
142     assert_eq!(trace[1].module_offset(), Some(0x21));
143     assert_eq!(e.downcast::<Trap>()?, Trap::UnreachableCodeReached);
144 
145     Ok(())
146 }
147 
148 #[test]
test_trap_through_host() -> Result<()>149 fn test_trap_through_host() -> Result<()> {
150     let wat = r#"
151         (module $hello_mod
152             (import "" "" (func $host_func_a))
153             (import "" "" (func $host_func_b))
154             (func $a (export "a")
155                 call $host_func_a
156             )
157             (func $b (export "b")
158                 call $host_func_b
159             )
160             (func $c (export "c")
161                 unreachable
162             )
163         )
164     "#;
165 
166     let engine = Engine::default();
167     let module = Module::new(&engine, wat)?;
168     let mut store = Store::<()>::new(&engine, ());
169 
170     let host_func_a = Func::new(
171         &mut store,
172         FuncType::new(&engine, vec![], vec![]),
173         |mut caller, _args, _results| {
174             caller
175                 .get_export("b")
176                 .unwrap()
177                 .into_func()
178                 .unwrap()
179                 .call(caller, &[], &mut [])?;
180             Ok(())
181         },
182     );
183     let host_func_b = Func::new(
184         &mut store,
185         FuncType::new(&engine, vec![], vec![]),
186         |mut caller, _args, _results| {
187             caller
188                 .get_export("c")
189                 .unwrap()
190                 .into_func()
191                 .unwrap()
192                 .call(caller, &[], &mut [])?;
193             Ok(())
194         },
195     );
196 
197     let instance = Instance::new(
198         &mut store,
199         &module,
200         &[host_func_a.into(), host_func_b.into()],
201     )?;
202     let a = instance.get_typed_func::<(), ()>(&mut store, "a")?;
203     let err = a.call(&mut store, ()).unwrap_err();
204     let trace = err.downcast_ref::<WasmBacktrace>().unwrap().frames();
205     assert_eq!(trace.len(), 3);
206     assert_eq!(trace[0].func_name(), Some("c"));
207     assert_eq!(trace[1].func_name(), Some("b"));
208     assert_eq!(trace[2].func_name(), Some("a"));
209     Ok(())
210 }
211 
212 #[test]
test_trap_backtrace_disabled() -> Result<()>213 fn test_trap_backtrace_disabled() -> Result<()> {
214     let mut config = Config::default();
215     config.wasm_backtrace_max_frames(None);
216     let engine = Engine::new(&config).unwrap();
217     let mut store = Store::<()>::new(&engine, ());
218     let wat = r#"
219         (module $hello_mod
220             (func (export "run") (call $hello))
221             (func $hello (unreachable))
222         )
223     "#;
224 
225     let module = Module::new(store.engine(), wat)?;
226     let instance = Instance::new(&mut store, &module, &[])?;
227     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
228 
229     let e = run_func.call(&mut store, ()).unwrap_err();
230     assert!(e.downcast_ref::<WasmBacktrace>().is_none());
231     Ok(())
232 }
233 
234 #[test]
test_trap_trace_cb() -> Result<()>235 fn test_trap_trace_cb() -> Result<()> {
236     let mut store = Store::<()>::default();
237     let wat = r#"
238         (module $hello_mod
239             (import "" "throw" (func $throw))
240             (func (export "run") (call $hello))
241             (func $hello (call $throw))
242         )
243     "#;
244 
245     let fn_type = FuncType::new(store.engine(), None, None);
246     let fn_func = Func::new(&mut store, fn_type, |_, _, _| bail!("cb throw"));
247 
248     let module = Module::new(store.engine(), wat)?;
249     let instance = Instance::new(&mut store, &module, &[fn_func.into()])?;
250     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
251 
252     let e = run_func.call(&mut store, ()).unwrap_err();
253 
254     let trace = e.downcast_ref::<WasmBacktrace>().unwrap().frames();
255     assert_eq!(trace.len(), 2);
256     assert_eq!(trace[0].module().name().unwrap(), "hello_mod");
257     assert_eq!(trace[0].func_index(), 2);
258     assert_eq!(trace[1].module().name().unwrap(), "hello_mod");
259     assert_eq!(trace[1].func_index(), 1);
260     e.assert_contains("cb throw");
261 
262     Ok(())
263 }
264 
265 #[test]
test_trap_stack_overflow() -> Result<()>266 fn test_trap_stack_overflow() -> Result<()> {
267     let max_frames = NonZeroUsize::new(32).unwrap();
268     let wat = r#"
269         (module $rec_mod
270             (func $run (export "run") (call $run))
271         )
272     "#;
273 
274     let mut config = Config::new();
275     config.wasm_backtrace_max_frames(Some(max_frames));
276     let engine = Engine::new(&config).unwrap();
277     let module = Module::new(&engine, wat).unwrap();
278     let mut store = Store::new(&engine, ());
279     let instance = Instance::new(&mut store, &module, &[]).unwrap();
280     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
281 
282     let e = run_func.call(&mut store, ()).unwrap_err();
283 
284     let trace = e.downcast_ref::<WasmBacktrace>().unwrap().frames();
285     assert_eq!(trace.len(), max_frames.get());
286     for i in 0..trace.len() {
287         assert_eq!(trace[i].module().name().unwrap(), "rec_mod");
288         assert_eq!(trace[i].func_index(), 0);
289         assert_eq!(trace[i].func_name(), Some("run"));
290     }
291     assert_eq!(e.downcast::<Trap>()?, Trap::StackOverflow);
292 
293     Ok(())
294 }
295 
296 #[test]
trap_display_pretty() -> Result<()>297 fn trap_display_pretty() -> Result<()> {
298     let mut store = Store::<()>::default();
299     let wat = r#"
300         (module $m
301             (func $die unreachable)
302             (func call $die)
303             (func $foo call 1)
304             (func (export "bar") call $foo)
305         )
306     "#;
307 
308     let module = Module::new(store.engine(), wat)?;
309     let instance = Instance::new(&mut store, &module, &[])?;
310     let run_func = instance.get_typed_func::<(), ()>(&mut store, "bar")?;
311 
312     let e = run_func.call(&mut store, ()).unwrap_err();
313     e.assert_contains(
314         "\
315 error while executing at wasm backtrace:
316     0:     0x23 - m!die
317     1:     0x27 - m!<wasm function 1>
318     2:     0x2c - m!foo
319     3:     0x31 - m!<wasm function 3>",
320     );
321 
322     e.assert_contains("wasm trap: wasm `unreachable` instruction executed");
323     Ok(())
324 }
325 
326 #[test]
trap_display_multi_module() -> Result<()>327 fn trap_display_multi_module() -> Result<()> {
328     let mut store = Store::<()>::default();
329     let wat = r#"
330         (module $a
331             (func $die unreachable)
332             (func call $die)
333             (func $foo call 1)
334             (func (export "bar") call $foo)
335         )
336     "#;
337 
338     let module = Module::new(store.engine(), wat)?;
339     let instance = Instance::new(&mut store, &module, &[])?;
340     let bar = instance.get_export(&mut store, "bar").unwrap();
341 
342     let wat = r#"
343         (module $b
344             (import "" "" (func $bar))
345             (func $middle call $bar)
346             (func (export "bar2") call $middle)
347         )
348     "#;
349     let module = Module::new(store.engine(), wat)?;
350     let instance = Instance::new(&mut store, &module, &[bar])?;
351     let bar2 = instance.get_typed_func::<(), ()>(&mut store, "bar2")?;
352 
353     let e = bar2.call(&mut store, ()).unwrap_err();
354     e.assert_contains(
355         "\
356 error while executing at wasm backtrace:
357     0:     0x23 - a!die
358     1:     0x27 - a!<wasm function 1>
359     2:     0x2c - a!foo
360     3:     0x31 - a!<wasm function 3>
361     4:     0x29 - b!middle
362     5:     0x2e - b!<wasm function 2>",
363     );
364     e.assert_contains("wasm trap: wasm `unreachable` instruction executed");
365     Ok(())
366 }
367 
368 #[test]
trap_start_function_import() -> Result<()>369 fn trap_start_function_import() -> Result<()> {
370     let mut store = Store::<()>::default();
371     let binary = wat::parse_str(
372         r#"
373             (module $a
374                 (import "" "" (func $foo))
375                 (start $foo)
376             )
377         "#,
378     )?;
379 
380     let module = Module::new(store.engine(), &binary)?;
381     let sig = FuncType::new(store.engine(), None, None);
382     let func = Func::new(&mut store, sig, |_, _, _| bail!("user trap"));
383     let err = Instance::new(&mut store, &module, &[func.into()]).unwrap_err();
384     err.assert_contains("user trap");
385     Ok(())
386 }
387 
388 #[test]
rust_panic_import() -> Result<()>389 fn rust_panic_import() -> Result<()> {
390     let mut store = Store::<()>::default();
391     let binary = wat::parse_str(
392         r#"
393             (module $a
394                 (import "" "" (func $foo))
395                 (import "" "" (func $bar))
396                 (func (export "foo") call $foo)
397                 (func (export "bar") call $bar)
398             )
399         "#,
400     )?;
401 
402     let module = Module::new(store.engine(), &binary)?;
403     let sig = FuncType::new(store.engine(), None, None);
404     let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic"));
405     let func2 = Func::wrap(&mut store, || -> () { panic!("this is another panic") });
406     let instance = Instance::new(&mut store, &module, &[func.into(), func2.into()])?;
407     let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
408     let err =
409         panic::catch_unwind(AssertUnwindSafe(|| drop(func.call(&mut store, ())))).unwrap_err();
410     assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
411 
412     let func = instance.get_typed_func::<(), ()>(&mut store, "bar")?;
413     let err = panic::catch_unwind(AssertUnwindSafe(|| {
414         drop(func.call(&mut store, ()));
415     }))
416     .unwrap_err();
417     assert_eq!(
418         err.downcast_ref::<&'static str>(),
419         Some(&"this is another panic")
420     );
421     Ok(())
422 }
423 
424 // Test that we properly save/restore our trampolines' saved Wasm registers
425 // (used when capturing backtraces) before we resume panics.
426 #[test]
rust_catch_panic_import() -> Result<()>427 fn rust_catch_panic_import() -> Result<()> {
428     let mut store = Store::<()>::default();
429 
430     let binary = wat::parse_str(
431         r#"
432             (module $a
433                 (import "" "panic" (func $panic))
434                 (import "" "catch panic" (func $catch_panic))
435                 (func (export "panic") call $panic)
436                 (func (export "run")
437                   call $catch_panic
438                   call $catch_panic
439                   unreachable
440                 )
441             )
442         "#,
443     )?;
444 
445     let module = Module::new(store.engine(), &binary)?;
446     let num_panics = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
447     let sig = FuncType::new(store.engine(), None, None);
448     let panic = Func::new(&mut store, sig, {
449         let num_panics = num_panics.clone();
450         move |_, _, _| {
451             num_panics.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
452             panic!("this is a panic");
453         }
454     });
455     let catch_panic = Func::wrap(&mut store, |mut caller: Caller<'_, _>| {
456         panic::catch_unwind(AssertUnwindSafe(|| {
457             drop(
458                 caller
459                     .get_export("panic")
460                     .unwrap()
461                     .into_func()
462                     .unwrap()
463                     .call(&mut caller, &[], &mut []),
464             );
465         }))
466         .unwrap_err();
467     });
468 
469     let instance = Instance::new(&mut store, &module, &[panic.into(), catch_panic.into()])?;
470     let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
471     let trap = run.call(&mut store, ()).unwrap_err();
472     let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
473     assert_eq!(trace.len(), 1);
474     assert_eq!(trace[0].func_index(), 3);
475     assert_eq!(num_panics.load(std::sync::atomic::Ordering::SeqCst), 2);
476     Ok(())
477 }
478 
479 #[test]
rust_panic_start_function() -> Result<()>480 fn rust_panic_start_function() -> Result<()> {
481     let mut store = Store::<()>::default();
482     let binary = wat::parse_str(
483         r#"
484             (module $a
485                 (import "" "" (func $foo))
486                 (start $foo)
487             )
488         "#,
489     )?;
490 
491     let module = Module::new(store.engine(), &binary)?;
492     let sig = FuncType::new(store.engine(), None, None);
493     let func = Func::new(&mut store, sig, |_, _, _| panic!("this is a panic"));
494     let err = panic::catch_unwind(AssertUnwindSafe(|| {
495         drop(Instance::new(&mut store, &module, &[func.into()]));
496     }))
497     .unwrap_err();
498     assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic"));
499 
500     let func = Func::wrap(&mut store, || -> () { panic!("this is another panic") });
501     let err = panic::catch_unwind(AssertUnwindSafe(|| {
502         drop(Instance::new(&mut store, &module, &[func.into()]));
503     }))
504     .unwrap_err();
505     assert_eq!(
506         err.downcast_ref::<&'static str>(),
507         Some(&"this is another panic")
508     );
509     Ok(())
510 }
511 
512 #[test]
mismatched_arguments() -> Result<()>513 fn mismatched_arguments() -> Result<()> {
514     let mut store = Store::<()>::default();
515     let binary = wat::parse_str(
516         r#"
517             (module $a
518                 (func (export "foo") (param i32))
519             )
520         "#,
521     )?;
522 
523     let module = Module::new(store.engine(), &binary)?;
524     let instance = Instance::new(&mut store, &module, &[])?;
525     let func = instance.get_func(&mut store, "foo").unwrap();
526     assert_eq!(
527         func.call(&mut store, &[], &mut []).unwrap_err().to_string(),
528         "expected 1 arguments, got 0"
529     );
530     let e = func.call(&mut store, &[Val::F32(0)], &mut []).unwrap_err();
531     e.assert_contains("argument type mismatch");
532     e.assert_contains("expected i32, found f32");
533     assert_eq!(
534         func.call(&mut store, &[Val::I32(0), Val::I32(1)], &mut [])
535             .unwrap_err()
536             .to_string(),
537         "expected 1 arguments, got 2"
538     );
539     Ok(())
540 }
541 
542 #[test]
call_signature_mismatch() -> Result<()>543 fn call_signature_mismatch() -> Result<()> {
544     let mut store = Store::<()>::default();
545     let binary = wat::parse_str(
546         r#"
547             (module $a
548                 (func $foo
549                     i32.const 0
550                     call_indirect)
551                 (func $bar (param i32))
552                 (start $foo)
553 
554                 (table 1 funcref)
555                 (elem (i32.const 0) 1)
556             )
557         "#,
558     )?;
559 
560     let module = Module::new(store.engine(), &binary)?;
561     let err = Instance::new(&mut store, &module, &[]).err().unwrap();
562     err.downcast_ref::<Trap>().unwrap();
563     err.assert_contains("wasm trap: indirect call type mismatch");
564     Ok(())
565 }
566 
567 #[test]
start_trap_pretty() -> Result<()>568 fn start_trap_pretty() -> Result<()> {
569     let mut store = Store::<()>::default();
570     let wat = r#"
571         (module $m
572             (func $die unreachable)
573             (func call $die)
574             (func $foo call 1)
575             (func $start call $foo)
576             (start $start)
577         )
578     "#;
579 
580     let module = Module::new(store.engine(), wat)?;
581     let e = match Instance::new(&mut store, &module, &[]) {
582         Ok(_) => panic!("expected failure"),
583         Err(e) => e,
584     };
585 
586     e.assert_contains(
587         "\
588 error while executing at wasm backtrace:
589     0:     0x1d - m!die
590     1:     0x21 - m!<wasm function 1>
591     2:     0x26 - m!foo
592     3:     0x2b - m!start",
593     );
594 
595     e.assert_contains("wasm trap: wasm `unreachable` instruction executed");
596     Ok(())
597 }
598 
599 #[test]
present_after_module_drop() -> Result<()>600 fn present_after_module_drop() -> Result<()> {
601     let mut store = Store::<()>::default();
602     let module = Module::new(store.engine(), r#"(func (export "foo") unreachable)"#)?;
603     let instance = Instance::new(&mut store, &module, &[])?;
604     let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
605 
606     println!("asserting before we drop modules");
607     assert_trap(func.call(&mut store, ()).unwrap_err());
608     drop((instance, module));
609 
610     println!("asserting after drop");
611     assert_trap(func.call(&mut store, ()).unwrap_err());
612     return Ok(());
613 
614     fn assert_trap(t: Error) {
615         println!("{t:?}");
616         let trace = t.downcast_ref::<WasmBacktrace>().unwrap().frames();
617         assert_eq!(trace.len(), 1);
618         assert_eq!(trace[0].func_index(), 0);
619     }
620 }
621 
assert_trap_code(wat: &str, code: wasmtime::Trap)622 fn assert_trap_code(wat: &str, code: wasmtime::Trap) {
623     let mut store = Store::<()>::default();
624     let module = Module::new(store.engine(), wat).unwrap();
625 
626     let err = match Instance::new(&mut store, &module, &[]) {
627         Ok(_) => unreachable!(),
628         Err(e) => e,
629     };
630     let trap = err.downcast_ref::<Trap>().unwrap();
631     assert_eq!(*trap, code);
632 
633     let trace = err.downcast_ref::<WasmBacktrace>().unwrap().frames();
634     assert!(trace.len() > 0);
635     assert_eq!(trace[0].func_index(), 0);
636     assert!(trace[0].func_offset().is_some());
637 }
638 
639 #[test]
trap_codes()640 fn trap_codes() {
641     assert_trap_code(
642         r#"
643             (module
644               (memory 0)
645               (func $start (drop (i32.load (i32.const 1000000))))
646               (start $start)
647             )
648          "#,
649         Trap::MemoryOutOfBounds,
650     );
651 
652     assert_trap_code(
653         r#"
654             (module
655               (memory 0)
656               (func $start (drop (i32.load memory.size)))
657               (start $start)
658             )
659          "#,
660         Trap::MemoryOutOfBounds,
661     );
662 
663     for (ty, min) in [("i32", i32::MIN as u32 as u64), ("i64", i64::MIN as u64)] {
664         for op in ["rem", "div"] {
665             for sign in ["u", "s"] {
666                 println!("testing {ty}.{op}_{sign}");
667                 assert_trap_code(
668                     &format!(
669                         r#"
670                            (module
671                              (func $div (param {ty} {ty}) (result {ty})
672                                local.get 0
673                                local.get 1
674                                {ty}.{op}_{sign})
675                              (func $start (drop (call $div ({ty}.const 1) ({ty}.const 0))))
676                              (start $start)
677                            )
678                         "#
679                     ),
680                     Trap::IntegerDivisionByZero,
681                 );
682             }
683         }
684 
685         println!("testing {ty}.div_s INT_MIN/-1");
686         assert_trap_code(
687             &format!(
688                 r#"
689                     (module
690                      (func $div (param {ty} {ty}) (result {ty})
691                       local.get 0
692                       local.get 1
693                       {ty}.div_s)
694                      (func $start (drop (call $div ({ty}.const {min}) ({ty}.const -1))))
695                      (start $start)
696                     )
697                 "#
698             ),
699             Trap::IntegerOverflow,
700         );
701     }
702 }
703 
rustc(src: &str) -> Vec<u8>704 fn rustc(src: &str) -> Vec<u8> {
705     let td = tempfile::TempDir::new().unwrap();
706     let output = td.path().join("foo.wasm");
707     let input = td.path().join("input.rs");
708     std::fs::write(&input, src).unwrap();
709     let result = Command::new("rustc")
710         .arg(&input)
711         .arg("-o")
712         .arg(&output)
713         .arg("--target")
714         .arg("wasm32-wasip1")
715         .arg("-g")
716         .output()
717         .unwrap();
718     if result.status.success() {
719         return std::fs::read(&output).unwrap();
720     }
721     panic!(
722         "rustc failed: {}\n{}",
723         result.status,
724         String::from_utf8_lossy(&result.stderr)
725     );
726 }
727 
728 #[test]
parse_dwarf_info() -> Result<()>729 fn parse_dwarf_info() -> Result<()> {
730     let wasm = rustc(
731         "
732             fn main() {
733                 panic!();
734             }
735         ",
736     );
737     let mut config = Config::new();
738     config.wasm_backtrace_details(WasmBacktraceDetails::Enable);
739     let engine = Engine::new(&config)?;
740     let module = Module::new(&engine, &wasm)?;
741     let mut linker = Linker::new(&engine);
742     wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |t| t)?;
743     let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
744     linker.module(&mut store, "", &module)?;
745     let run = linker.get_default(&mut store, "")?;
746     let trap = run.call(&mut store, &[], &mut []).unwrap_err();
747 
748     let mut found = false;
749     let frames = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
750     for frame in frames {
751         for symbol in frame.symbols() {
752             if let Some(file) = symbol.file() {
753                 if file.ends_with("input.rs") {
754                     found = true;
755                     assert!(symbol.name().unwrap().contains("main"));
756                     assert_eq!(symbol.line(), Some(3));
757                 }
758             }
759         }
760     }
761     assert!(found);
762     Ok(())
763 }
764 
765 #[test]
no_hint_even_with_dwarf_info() -> Result<()>766 fn no_hint_even_with_dwarf_info() -> Result<()> {
767     let mut config = Config::new();
768     config.wasm_backtrace_details(WasmBacktraceDetails::Disable);
769     let engine = Engine::new(&config)?;
770     let mut store = Store::new(&engine, ());
771     let module = Module::new(
772         &engine,
773         r#"
774             (module
775                 (@custom ".debug_info" (after last) "")
776                 (func $start
777                     unreachable)
778                 (start $start)
779             )
780         "#,
781     )?;
782     let trap = Instance::new(&mut store, &module, &[]).unwrap_err();
783     trap.assert_contains(
784         "\
785 error while executing at wasm backtrace:
786     0:     0x1a - <unknown>!start",
787     );
788 
789     trap.assert_contains("wasm trap: wasm `unreachable` instruction executed");
790     assert!(!format!("{trap:?}").contains("WASM_BACKTRACE_DETAILS"));
791     Ok(())
792 }
793 
794 #[test]
hint_with_dwarf_info() -> Result<()>795 fn hint_with_dwarf_info() -> Result<()> {
796     // Skip this test if the env var is already configure, but in CI we're sure
797     // to run tests without this env var configured.
798     if std::env::var("WASMTIME_BACKTRACE_DETAILS").is_ok() {
799         return Ok(());
800     }
801     let mut store = Store::<()>::default();
802     let module = Module::new(
803         store.engine(),
804         r#"
805             (module
806                 (@custom ".debug_info" (after last) "")
807                 (func $start
808                     unreachable)
809                 (start $start)
810             )
811         "#,
812     )?;
813     let trap = Instance::new(&mut store, &module, &[]).unwrap_err();
814     trap.assert_contains(
815         "\
816 error while executing at wasm backtrace:
817     0:     0x1a - <unknown>!start
818 note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information");
819     trap.assert_contains("wasm trap: wasm `unreachable` instruction executed");
820     Ok(())
821 }
822 
823 #[test]
multithreaded_traps() -> Result<()>824 fn multithreaded_traps() -> Result<()> {
825     // Compile and run unreachable on a thread, then moves over the whole store to another thread,
826     // and make sure traps are still correctly caught after notifying the store of the move.
827     let mut store = Store::<()>::default();
828     let module = Module::new(
829         store.engine(),
830         r#"(module (func (export "run") unreachable))"#,
831     )?;
832     let instance = Instance::new(&mut store, &module, &[])?;
833 
834     assert!(
835         instance
836             .get_typed_func::<(), ()>(&mut store, "run")?
837             .call(&mut store, ())
838             .is_err()
839     );
840 
841     let handle = std::thread::spawn(move || {
842         assert!(
843             instance
844                 .get_typed_func::<(), ()>(&mut store, "run")
845                 .unwrap()
846                 .call(&mut store, ())
847                 .is_err()
848         );
849     });
850 
851     handle.join().expect("couldn't join thread");
852 
853     Ok(())
854 }
855 
856 #[test]
traps_without_address_map() -> Result<()>857 fn traps_without_address_map() -> Result<()> {
858     let mut config = Config::new();
859     config.generate_address_map(false);
860     let engine = Engine::new(&config)?;
861     let mut store = Store::new(&engine, ());
862     let wat = r#"
863         (module $hello_mod
864             (func (export "run") (call $hello))
865             (func $hello (unreachable))
866         )
867     "#;
868 
869     let module = Module::new(store.engine(), wat)?;
870     let instance = Instance::new(&mut store, &module, &[])?;
871     let run_func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
872 
873     let e = run_func.call(&mut store, ()).unwrap_err();
874 
875     let trace = e.downcast_ref::<WasmBacktrace>().unwrap().frames();
876     assert_eq!(trace.len(), 2);
877     assert_eq!(trace[0].func_name(), Some("hello"));
878     assert_eq!(trace[0].func_index(), 1);
879     assert_eq!(trace[0].module_offset(), None);
880     assert_eq!(trace[1].func_name(), None);
881     assert_eq!(trace[1].func_index(), 0);
882     assert_eq!(trace[1].module_offset(), None);
883     Ok(())
884 }
885 
886 #[test]
catch_trap_calling_across_stores() -> Result<()>887 fn catch_trap_calling_across_stores() -> Result<()> {
888     let _ = env_logger::try_init();
889 
890     let engine = Engine::default();
891 
892     let mut child_store = Store::new(&engine, ());
893     let child_module = Module::new(
894         child_store.engine(),
895         r#"
896             (module $child
897               (func $trap (export "trap")
898                 unreachable
899               )
900             )
901         "#,
902     )?;
903     let child_instance = Instance::new(&mut child_store, &child_module, &[])?;
904 
905     struct ParentCtx {
906         child_store: Store<()>,
907         child_instance: Instance,
908     }
909 
910     let mut linker = Linker::new(&engine);
911     linker.func_wrap(
912         "host",
913         "catch_child_trap",
914         move |mut caller: Caller<'_, ParentCtx>| {
915             let mut ctx = caller.as_context_mut();
916             let data = ctx.data_mut();
917             let func = data
918                 .child_instance
919                 .get_typed_func::<(), ()>(&mut data.child_store, "trap")
920                 .expect("trap function should be exported");
921 
922             let trap = func.call(&mut data.child_store, ()).unwrap_err();
923             trap.assert_contains("unreachable");
924 
925             let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
926 
927             assert_eq!(trace.len(), 1);
928             assert_eq!(trace[0].func_name(), Some("trap"));
929             // For now, we only get stack frames for Wasm in this store, not
930             // across all stores.
931             //
932             // assert_eq!(trace[1].func_name(), Some("run"));
933 
934             Ok(())
935         },
936     )?;
937 
938     let mut store = Store::new(
939         &engine,
940         ParentCtx {
941             child_store,
942             child_instance,
943         },
944     );
945 
946     let parent_module = Module::new(
947         store.engine(),
948         r#"
949             (module $parent
950               (func $host.catch_child_trap (import "host" "catch_child_trap"))
951               (func $run (export "run")
952                 call $host.catch_child_trap
953               )
954             )
955         "#,
956     )?;
957 
958     let parent_instance = linker.instantiate(&mut store, &parent_module)?;
959 
960     let func = parent_instance.get_typed_func::<(), ()>(&mut store, "run")?;
961     func.call(store, ())?;
962 
963     Ok(())
964 }
965 
966 #[tokio::test]
async_then_sync_trap() -> Result<()>967 async fn async_then_sync_trap() -> Result<()> {
968     // Test the trapping and capturing the stack with the following sequence of
969     // calls:
970     //
971     // a[async] ---> b[host] ---> c[sync]
972 
973     drop(env_logger::try_init());
974 
975     let wat = r#"
976         (module
977             (import "" "b" (func $b))
978             (func $a (export "a")
979                 call $b
980             )
981             (func $c (export "c")
982                 unreachable
983             )
984         )
985     "#;
986 
987     let mut sync_store = Store::new(&Engine::default(), ());
988 
989     let sync_module = Module::new(sync_store.engine(), wat)?;
990 
991     let mut sync_linker = Linker::new(sync_store.engine());
992     sync_linker.func_wrap("", "b", |_caller: Caller<_>| -> () { unreachable!() })?;
993 
994     let sync_instance = sync_linker.instantiate(&mut sync_store, &sync_module)?;
995 
996     struct AsyncCtx {
997         sync_instance: Instance,
998         sync_store: Store<()>,
999     }
1000 
1001     let mut async_store = Store::new(
1002         &Engine::default(),
1003         AsyncCtx {
1004             sync_instance,
1005             sync_store,
1006         },
1007     );
1008 
1009     let async_module = Module::new(async_store.engine(), wat)?;
1010 
1011     let mut async_linker = Linker::new(async_store.engine());
1012     async_linker.func_wrap("", "b", move |mut caller: Caller<AsyncCtx>| {
1013         log::info!("Called `b`...");
1014         let sync_instance = caller.data().sync_instance;
1015         let sync_store = &mut caller.data_mut().sync_store;
1016 
1017         log::info!("Calling `c`...");
1018         let c = sync_instance
1019             .get_typed_func::<(), ()>(&mut *sync_store, "c")
1020             .unwrap();
1021         c.call(sync_store, ())?;
1022         Ok(())
1023     })?;
1024 
1025     let async_instance = async_linker
1026         .instantiate_async(&mut async_store, &async_module)
1027         .await?;
1028 
1029     log::info!("Calling `a`...");
1030     let a = async_instance
1031         .get_typed_func::<(), ()>(&mut async_store, "a")
1032         .unwrap();
1033     let trap = a.call_async(&mut async_store, ()).await.unwrap_err();
1034 
1035     let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
1036     // We don't support cross-store or cross-engine symbolication currently, so
1037     // the other frames are ignored.
1038     assert_eq!(trace.len(), 1);
1039     assert_eq!(trace[0].func_name(), Some("c"));
1040 
1041     Ok(())
1042 }
1043 
1044 #[tokio::test(flavor = "multi_thread")]
sync_then_async_trap() -> Result<()>1045 async fn sync_then_async_trap() -> Result<()> {
1046     // Test the trapping and capturing the stack with the following sequence of
1047     // calls:
1048     //
1049     // a[sync] ---> b[host] ---> c[async]
1050 
1051     drop(env_logger::try_init());
1052 
1053     let wat = r#"
1054         (module
1055             (import "" "b" (func $b))
1056             (func $a (export "a")
1057                 call $b
1058             )
1059             (func $c (export "c")
1060                 unreachable
1061             )
1062         )
1063     "#;
1064 
1065     let mut async_store = Store::new(&Engine::default(), ());
1066 
1067     let async_module = Module::new(async_store.engine(), wat)?;
1068 
1069     let mut async_linker = Linker::new(async_store.engine());
1070     async_linker.func_wrap("", "b", |_caller: Caller<_>| -> () { unreachable!() })?;
1071 
1072     let async_instance = async_linker
1073         .instantiate_async(&mut async_store, &async_module)
1074         .await?;
1075 
1076     struct SyncCtx {
1077         async_instance: Instance,
1078         async_store: Store<()>,
1079     }
1080 
1081     let mut sync_store = Store::new(
1082         &Engine::default(),
1083         SyncCtx {
1084             async_instance,
1085             async_store,
1086         },
1087     );
1088 
1089     let sync_module = Module::new(sync_store.engine(), wat)?;
1090 
1091     let mut sync_linker = Linker::new(sync_store.engine());
1092     sync_linker.func_wrap("", "b", move |mut caller: Caller<SyncCtx>| -> Result<()> {
1093         log::info!("Called `b`...");
1094         let async_instance = caller.data().async_instance;
1095         let async_store = &mut caller.data_mut().async_store;
1096 
1097         log::info!("Calling `c`...");
1098         let c = async_instance
1099             .get_typed_func::<(), ()>(&mut *async_store, "c")
1100             .unwrap();
1101         tokio::task::block_in_place(|| {
1102             tokio::runtime::Handle::current()
1103                 .block_on(async move { c.call_async(async_store, ()).await })
1104         })?;
1105         Ok(())
1106     })?;
1107 
1108     let sync_instance = sync_linker.instantiate(&mut sync_store, &sync_module)?;
1109 
1110     log::info!("Calling `a`...");
1111     let a = sync_instance
1112         .get_typed_func::<(), ()>(&mut sync_store, "a")
1113         .unwrap();
1114     let trap = a.call(&mut sync_store, ()).unwrap_err();
1115 
1116     let trace = trap.downcast_ref::<WasmBacktrace>().unwrap().frames();
1117     // We don't support cross-store or cross-engine symbolication currently, so
1118     // the other frames are ignored.
1119     assert_eq!(trace.len(), 1);
1120     assert_eq!(trace[0].func_name(), Some("c"));
1121 
1122     Ok(())
1123 }
1124 
1125 #[test]
standalone_backtrace() -> Result<()>1126 fn standalone_backtrace() -> Result<()> {
1127     let engine = Engine::default();
1128     let mut store = Store::new(&engine, ());
1129     let trace = WasmBacktrace::capture(&store);
1130     assert!(trace.frames().is_empty());
1131     let module = Module::new(
1132         &engine,
1133         r#"
1134             (module
1135                 (import "" "" (func $host))
1136                 (func $foo (export "f") call $bar)
1137                 (func $bar call $host)
1138             )
1139         "#,
1140     )?;
1141     let func = Func::wrap(&mut store, |cx: Caller<'_, ()>| {
1142         let trace = WasmBacktrace::capture(&cx);
1143         assert_eq!(trace.frames().len(), 2);
1144         let frame1 = &trace.frames()[0];
1145         let frame2 = &trace.frames()[1];
1146         assert_eq!(frame1.func_index(), 2);
1147         assert_eq!(frame1.func_name(), Some("bar"));
1148         assert_eq!(frame2.func_index(), 1);
1149         assert_eq!(frame2.func_name(), Some("foo"));
1150     });
1151     let instance = Instance::new(&mut store, &module, &[func.into()])?;
1152     let f = instance.get_typed_func::<(), ()>(&mut store, "f")?;
1153     f.call(&mut store, ())?;
1154     Ok(())
1155 }
1156 
1157 #[test]
standalone_backtrace_disabled() -> Result<()>1158 fn standalone_backtrace_disabled() -> Result<()> {
1159     let mut config = Config::new();
1160     config.wasm_backtrace_max_frames(None);
1161     let engine = Engine::new(&config)?;
1162     let mut store = Store::new(&engine, ());
1163     let module = Module::new(
1164         &engine,
1165         r#"
1166             (module
1167                 (import "" "" (func $host))
1168                 (func $foo (export "f") call $bar)
1169                 (func $bar call $host)
1170             )
1171         "#,
1172     )?;
1173     let func = Func::wrap(&mut store, |cx: Caller<'_, ()>| {
1174         let trace = WasmBacktrace::capture(&cx);
1175         assert_eq!(trace.frames().len(), 0);
1176         let trace = WasmBacktrace::force_capture(&cx);
1177         assert_eq!(trace.frames().len(), 2);
1178     });
1179     let instance = Instance::new(&mut store, &module, &[func.into()])?;
1180     let f = instance.get_typed_func::<(), ()>(&mut store, "f")?;
1181     f.call(&mut store, ())?;
1182     Ok(())
1183 }
1184 
1185 #[test]
host_return_error_no_backtrace() -> Result<()>1186 fn host_return_error_no_backtrace() -> Result<()> {
1187     let mut config = Config::new();
1188     config.wasm_backtrace_max_frames(None);
1189     let engine = Engine::new(&config)?;
1190     let mut store = Store::new(&engine, ());
1191     let module = Module::new(
1192         &engine,
1193         r#"
1194             (module
1195                 (import "" "" (func $host))
1196                 (func $foo (export "f") call $bar)
1197                 (func $bar call $host)
1198             )
1199         "#,
1200     )?;
1201     let func = Func::wrap(&mut store, |_cx: Caller<'_, ()>| -> Result<()> {
1202         bail!("test")
1203     });
1204     let instance = Instance::new(&mut store, &module, &[func.into()])?;
1205     let f = instance.get_typed_func::<(), ()>(&mut store, "f")?;
1206     assert!(f.call(&mut store, ()).is_err());
1207     Ok(())
1208 }
1209 
1210 #[test]
div_plus_load_reported_right() -> Result<()>1211 fn div_plus_load_reported_right() -> Result<()> {
1212     let engine = Engine::default();
1213     let mut store = Store::new(&engine, ());
1214     let module = Module::new(
1215         &engine,
1216         r#"
1217             (module
1218                 (memory (export "memory") 1)
1219                 (func (export "i32.div_s") (param i32 i32) (result i32)
1220                     (i32.div_s (local.get 0) (i32.load (local.get 1))))
1221                 (func (export "i32.div_u") (param i32 i32) (result i32)
1222                     (i32.div_u (local.get 0) (i32.load (local.get 1))))
1223                 (func (export "i32.rem_s") (param i32 i32) (result i32)
1224                     (i32.rem_s (local.get 0) (i32.load (local.get 1))))
1225                 (func (export "i32.rem_u") (param i32 i32) (result i32)
1226                     (i32.rem_u (local.get 0) (i32.load (local.get 1))))
1227             )
1228         "#,
1229     )?;
1230     let instance = Instance::new(&mut store, &module, &[])?;
1231     let memory = instance.get_memory(&mut store, "memory").unwrap();
1232     let i32_div_s = instance.get_typed_func::<(i32, i32), i32>(&mut store, "i32.div_s")?;
1233     let i32_div_u = instance.get_typed_func::<(u32, u32), u32>(&mut store, "i32.div_u")?;
1234     let i32_rem_s = instance.get_typed_func::<(i32, i32), i32>(&mut store, "i32.rem_s")?;
1235     let i32_rem_u = instance.get_typed_func::<(u32, u32), u32>(&mut store, "i32.rem_u")?;
1236 
1237     memory.write(&mut store, 0, &1i32.to_le_bytes()).unwrap();
1238     memory.write(&mut store, 4, &0i32.to_le_bytes()).unwrap();
1239     memory.write(&mut store, 8, &(-1i32).to_le_bytes()).unwrap();
1240 
1241     assert_eq!(i32_div_s.call(&mut store, (100, 0))?, 100);
1242     assert_eq!(i32_div_u.call(&mut store, (101, 0))?, 101);
1243     assert_eq!(i32_rem_s.call(&mut store, (102, 0))?, 0);
1244     assert_eq!(i32_rem_u.call(&mut store, (103, 0))?, 0);
1245 
1246     assert_trap(
1247         i32_div_s.call(&mut store, (100, 4)),
1248         Trap::IntegerDivisionByZero,
1249     );
1250     assert_trap(
1251         i32_div_u.call(&mut store, (100, 4)),
1252         Trap::IntegerDivisionByZero,
1253     );
1254     assert_trap(
1255         i32_rem_s.call(&mut store, (100, 4)),
1256         Trap::IntegerDivisionByZero,
1257     );
1258     assert_trap(
1259         i32_rem_u.call(&mut store, (100, 4)),
1260         Trap::IntegerDivisionByZero,
1261     );
1262 
1263     assert_trap(
1264         i32_div_s.call(&mut store, (i32::MIN, 8)),
1265         Trap::IntegerOverflow,
1266     );
1267     assert_eq!(i32_rem_s.call(&mut store, (i32::MIN, 8))?, 0);
1268 
1269     assert_trap(
1270         i32_div_s.call(&mut store, (100, 100_000)),
1271         Trap::MemoryOutOfBounds,
1272     );
1273     assert_trap(
1274         i32_div_u.call(&mut store, (100, 100_000)),
1275         Trap::MemoryOutOfBounds,
1276     );
1277     assert_trap(
1278         i32_rem_s.call(&mut store, (100, 100_000)),
1279         Trap::MemoryOutOfBounds,
1280     );
1281     assert_trap(
1282         i32_rem_u.call(&mut store, (100, 100_000)),
1283         Trap::MemoryOutOfBounds,
1284     );
1285 
1286     return Ok(());
1287 
1288     #[track_caller]
1289     fn assert_trap<T>(result: Result<T>, expected: Trap) {
1290         match result {
1291             Ok(_) => panic!("expected failure"),
1292             Err(e) => {
1293                 if let Some(code) = e.downcast_ref::<Trap>() {
1294                     if *code == expected {
1295                         return;
1296                     }
1297                 }
1298                 panic!("unexpected error {e:?}");
1299             }
1300         }
1301     }
1302 }
1303 
1304 #[test]
wasm_fault_address_reported_by_default() -> Result<()>1305 fn wasm_fault_address_reported_by_default() -> Result<()> {
1306     let engine = Engine::default();
1307     let mut store = Store::new(&engine, ());
1308     let module = Module::new(
1309         &engine,
1310         r#"
1311             (module
1312                 (memory 1)
1313                 (func $start
1314                     i32.const 0xdeadbeef
1315                     i32.load
1316                     drop)
1317                 (start $start)
1318             )
1319         "#,
1320     )?;
1321     let err = Instance::new(&mut store, &module, &[]).unwrap_err();
1322 
1323     // NB: at this time there's no programmatic access to the fault address
1324     // because it's not always available for load/store traps. Only static
1325     // memories on 32-bit have this information, but bounds-checked memories use
1326     // manual trapping instructions and otherwise don't have a means of
1327     // communicating the faulting address at this time.
1328     //
1329     // It looks like the exact reported fault address may not be deterministic,
1330     // so assert that we have the right error message, but not the exact
1331     // address.
1332     //
1333     // Skip 32-bit platforms here which currently all use Pulley and don't use
1334     // virtual memory for catching traps. This means that the trap error isn't
1335     // available.
1336     let err = format!("{err:?}");
1337     let contains_address = err.contains("memory fault at wasm address ")
1338         && err.contains(" in linear memory of size 0x10000");
1339     let address_expected = cfg!(target_pointer_width = "64");
1340     assert_eq!(contains_address, address_expected, "bad error: {err}");
1341     Ok(())
1342 }
1343 
1344 #[cfg(target_arch = "x86_64")]
1345 #[test]
wasm_fault_address_reported_from_mpk_protected_memory() -> Result<()>1346 fn wasm_fault_address_reported_from_mpk_protected_memory() -> Result<()> {
1347     // Trigger the case where an OOB memory access causes a segfault and the
1348     // store attempts to convert it into a `WasmFault`, calculating the Wasm
1349     // address from the raw faulting address. Previously, a store could not do
1350     // this calculation for MPK-protected, causing an abort.
1351     let mut pool = crate::small_pool_config();
1352     pool.total_memories(16);
1353     pool.memory_protection_keys(Enabled::Auto);
1354     let mut config = Config::new();
1355     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
1356     let engine = Engine::new(&config)?;
1357 
1358     let mut store = Store::new(&engine, ());
1359     let module = Module::new(
1360         &engine,
1361         r#"
1362             (module
1363                 (memory 1)
1364                 (func $start
1365                     i32.const 0xdeadbeef
1366                     i32.load
1367                     drop)
1368                 (start $start)
1369             )
1370         "#,
1371     )?;
1372     let err = Instance::new(&mut store, &module, &[]).unwrap_err();
1373 
1374     // We expect an error here, not an abort; but we also check that the store
1375     // can now calculate the correct Wasm address. If this test is failing with
1376     // an abort, use `--nocapture` to see more details.
1377     err.assert_contains("0xdeadbeef");
1378     Ok(())
1379 }
1380 
1381 #[test]
trap_with_array_to_wasm_stack_args() -> Result<()>1382 fn trap_with_array_to_wasm_stack_args() -> Result<()> {
1383     let engine = Engine::default();
1384     let mut store = Store::new(&engine, ());
1385     let module = Module::new(
1386         &engine,
1387         r#"
1388             (module
1389                 (func $trap
1390                     unreachable)
1391                 (func $run (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1392                     call $trap)
1393                 (export "run" (func $run))
1394             )
1395         "#,
1396     )?;
1397 
1398     let instance = Instance::new(&mut store, &module, &[])?;
1399     let run = instance.get_func(&mut store, "run").unwrap();
1400 
1401     let err = run
1402         .call(
1403             &mut store,
1404             &[
1405                 Val::I64(0),
1406                 Val::I64(0),
1407                 Val::I64(0),
1408                 Val::I64(0),
1409                 Val::I64(0),
1410                 Val::I64(0),
1411                 Val::I64(0),
1412                 Val::I64(0),
1413                 Val::I64(0),
1414                 Val::I64(0),
1415                 Val::I64(0),
1416                 Val::I64(0),
1417                 Val::I64(0),
1418                 Val::I64(0),
1419                 Val::I64(0),
1420             ],
1421             &mut [],
1422         )
1423         .unwrap_err();
1424     assert!(err.is::<Trap>());
1425 
1426     let trace = err.downcast_ref::<WasmBacktrace>().unwrap();
1427     assert_eq!(trace.frames().len(), 2);
1428     assert_eq!(trace.frames()[0].func_name(), Some("trap"));
1429     assert_eq!(trace.frames()[1].func_name(), Some("run"));
1430 
1431     Ok(())
1432 }
1433 
1434 #[test]
trap_with_native_to_wasm_stack_args() -> Result<()>1435 fn trap_with_native_to_wasm_stack_args() -> Result<()> {
1436     let engine = Engine::default();
1437     let mut store = Store::new(&engine, ());
1438     let module = Module::new(
1439         &engine,
1440         r#"
1441             (module
1442                 (func $trap
1443                     unreachable)
1444                 (func $run (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1445                     call $trap)
1446                 (export "run" (func $run))
1447             )
1448         "#,
1449     )?;
1450 
1451     let instance = Instance::new(&mut store, &module, &[])?;
1452     let run = instance.get_func(&mut store, "run").unwrap();
1453 
1454     let err = run
1455         .typed::<(
1456             i64,
1457             i64,
1458             i64,
1459             i64,
1460             i64,
1461             i64,
1462             i64,
1463             i64,
1464             i64,
1465             i64,
1466             i64,
1467             i64,
1468             i64,
1469             i64,
1470             i64,
1471         ), ()>(&mut store)?
1472         .call(&mut store, (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
1473         .unwrap_err();
1474     assert!(err.is::<Trap>());
1475 
1476     let trace = err.downcast_ref::<WasmBacktrace>().unwrap();
1477     assert_eq!(trace.frames().len(), 2);
1478     assert_eq!(trace.frames()[0].func_name(), Some("trap"));
1479     assert_eq!(trace.frames()[1].func_name(), Some("run"));
1480 
1481     Ok(())
1482 }
1483 
1484 #[test]
dont_see_stale_stack_walking_registers() -> Result<()>1485 fn dont_see_stale_stack_walking_registers() -> Result<()> {
1486     let engine = Engine::default();
1487 
1488     let module = Module::new(
1489         &engine,
1490         r#"
1491             (module
1492                 (import "" "host_start" (func $host_start))
1493                 (import "" "host_get_trap" (func $host_get_trap))
1494                 (export "get_trap" (func $host_get_trap))
1495 
1496                 ;; We enter and exit Wasm, which saves registers in the
1497                 ;; `VMStoreContext`. Later, when we call a re-exported host
1498                 ;; function, we should not accidentally reuse those saved
1499                 ;; registers.
1500                 (start $start)
1501                 (func $start
1502                     (call $host_start)
1503                 )
1504             )
1505         "#,
1506     )?;
1507 
1508     let mut store = Store::new(&engine, ());
1509     let mut linker = Linker::new(&engine);
1510 
1511     let host_start = Func::new(
1512         &mut store,
1513         FuncType::new(&engine, [], []),
1514         |_caller, _args, _results| Ok(()),
1515     );
1516     linker.define(&store, "", "host_start", host_start)?;
1517 
1518     let host_get_trap = Func::new(
1519         &mut store,
1520         FuncType::new(&engine, [], []),
1521         |_caller, _args, _results| Err(wasmtime::format_err!("trap!!!")),
1522     );
1523     linker.define(&store, "", "host_get_trap", host_get_trap)?;
1524 
1525     let instance = linker.instantiate(&mut store, &module)?;
1526     let get_trap = instance.get_func(&mut store, "get_trap").unwrap();
1527 
1528     let err = get_trap.call(&mut store, &[], &mut []).unwrap_err();
1529     err.assert_contains("trap!!!");
1530 
1531     Ok(())
1532 }
1533 
1534 #[test]
same_module_multiple_stores() -> Result<()>1535 fn same_module_multiple_stores() -> Result<()> {
1536     let _ = env_logger::try_init();
1537 
1538     let engine = Engine::default();
1539 
1540     let module = Module::new(
1541         &engine,
1542         r#"
1543             (module
1544                 (import "" "f" (func $f))
1545                 (import "" "call_ref" (func $call_ref (param funcref)))
1546                 (global $g (mut i32) (i32.const 0))
1547                 (func $a (export "a")
1548                     call $b
1549                 )
1550                 (func $b
1551                     call $c
1552                 )
1553                 (func $c
1554                     global.get $g
1555                     if
1556                         call $f
1557                     else
1558                         i32.const 1
1559                         global.set $g
1560                         ref.func $a
1561                         call $call_ref
1562                     end
1563                 )
1564             )
1565         "#,
1566     )?;
1567 
1568     let stacks = Arc::new(Mutex::new(vec![]));
1569 
1570     let mut store3 = Store::new(&engine, ());
1571     let f3 = Func::new(&mut store3, FuncType::new(&engine, [], []), {
1572         let stacks = stacks.clone();
1573         move |caller, _params, _results| {
1574             stacks
1575                 .lock()
1576                 .unwrap()
1577                 .push(WasmBacktrace::force_capture(caller));
1578             Ok(())
1579         }
1580     });
1581     let call_ref3 = Func::wrap(&mut store3, |caller: Caller<'_, _>, f: Option<Func>| {
1582         f.unwrap().call(caller, &[], &mut [])
1583     });
1584     let instance3 = Instance::new(&mut store3, &module, &[f3.into(), call_ref3.into()])?;
1585 
1586     let mut store2 = Store::new(&engine, store3);
1587     let f2 = Func::new(&mut store2, FuncType::new(&engine, [], []), {
1588         let stacks = stacks.clone();
1589         move |mut caller, _params, _results| {
1590             stacks
1591                 .lock()
1592                 .unwrap()
1593                 .push(WasmBacktrace::force_capture(&mut caller));
1594             instance3
1595                 .get_typed_func::<(), ()>(caller.data_mut(), "a")
1596                 .unwrap()
1597                 .call(caller.data_mut(), ())
1598                 .unwrap();
1599             Ok(())
1600         }
1601     });
1602     let call_ref2 = Func::wrap(&mut store2, |caller: Caller<'_, _>, f: Option<Func>| {
1603         f.unwrap().call(caller, &[], &mut [])
1604     });
1605     let instance2 = Instance::new(&mut store2, &module, &[f2.into(), call_ref2.into()])?;
1606 
1607     let mut store1 = Store::new(&engine, store2);
1608     let f1 = Func::new(&mut store1, FuncType::new(&engine, [], []), {
1609         let stacks = stacks.clone();
1610         move |mut caller, _params, _results| {
1611             stacks
1612                 .lock()
1613                 .unwrap()
1614                 .push(WasmBacktrace::force_capture(&mut caller));
1615             instance2
1616                 .get_typed_func::<(), ()>(caller.data_mut(), "a")
1617                 .unwrap()
1618                 .call(caller.data_mut(), ())
1619                 .unwrap();
1620             Ok(())
1621         }
1622     });
1623     let call_ref1 = Func::wrap(&mut store1, |caller: Caller<'_, _>, f: Option<Func>| {
1624         f.unwrap().call(caller, &[], &mut [])
1625     });
1626     let instance1 = Instance::new(&mut store1, &module, &[f1.into(), call_ref1.into()])?;
1627 
1628     instance1
1629         .get_typed_func::<(), ()>(&mut store1, "a")?
1630         .call(&mut store1, ())?;
1631 
1632     let expected_stacks = vec![
1633         // [f1, c1, b1, a1, call_ref1, c1, b1, a1]
1634         vec!["c", "b", "a", "c", "b", "a"],
1635         // [f2, c2, b2, a2, call_ref2, c2, b2, a2, f1, c1, b1, a1, call_ref1, c1, b1, a1]
1636         vec!["c", "b", "a", "c", "b", "a"],
1637         // [f3, c3, b3, a3, call_ref3, c3, b3, a3, f2, c2, b2, a2, call_ref2, c2, b2, a2, f1, c1, b1, a1, call_ref1, c1, b1, a1]
1638         vec!["c", "b", "a", "c", "b", "a"],
1639     ];
1640     eprintln!("expected = {expected_stacks:#?}");
1641     let actual_stacks = stacks.lock().unwrap();
1642     eprintln!("actual = {actual_stacks:#?}");
1643 
1644     assert_eq!(actual_stacks.len(), expected_stacks.len());
1645     for (expected_stack, actual_stack) in expected_stacks.into_iter().zip(actual_stacks.iter()) {
1646         assert_eq!(expected_stack.len(), actual_stack.frames().len());
1647         for (expected_frame, actual_frame) in expected_stack.into_iter().zip(actual_stack.frames())
1648         {
1649             assert_eq!(actual_frame.func_name(), Some(expected_frame));
1650         }
1651     }
1652 
1653     Ok(())
1654 }
1655 
1656 #[wasmtime_test(wasm_features(tail_call))]
tail_call_to_imported_function(config: &mut Config) -> Result<()>1657 fn tail_call_to_imported_function(config: &mut Config) -> Result<()> {
1658     let engine = Engine::new(config)?;
1659 
1660     let module = Module::new(
1661         &engine,
1662         r#"
1663             (module
1664               (import "" "" (func (result i32)))
1665 
1666               (func (export "run") (result i32)
1667                 return_call 0
1668               )
1669             )
1670         "#,
1671     )?;
1672 
1673     let mut store = Store::new(&engine, ());
1674     let host_func = Func::wrap(&mut store, || -> Result<i32> { bail!("whoopsie") });
1675     let instance = Instance::new(&mut store, &module, &[host_func.into()])?;
1676 
1677     let run = instance.get_typed_func::<(), i32>(&mut store, "run")?;
1678     let err = run.call(&mut store, ()).unwrap_err();
1679     err.assert_contains("whoopsie");
1680 
1681     Ok(())
1682 }
1683 
1684 #[wasmtime_test(wasm_features(tail_call))]
tail_call_to_imported_function_in_start_function(config: &mut Config) -> Result<()>1685 fn tail_call_to_imported_function_in_start_function(config: &mut Config) -> Result<()> {
1686     let engine = Engine::new(config)?;
1687 
1688     let module = Module::new(
1689         &engine,
1690         r#"
1691             (module
1692               (import "" "" (func))
1693 
1694               (func $f
1695                 return_call 0
1696               )
1697 
1698               (start $f)
1699             )
1700         "#,
1701     )?;
1702 
1703     let mut store = Store::new(&engine, ());
1704     let host_func = Func::wrap(&mut store, || -> Result<()> { bail!("whoopsie") });
1705     let err = Instance::new(&mut store, &module, &[host_func.into()]).unwrap_err();
1706     err.assert_contains("whoopsie");
1707 
1708     Ok(())
1709 }
1710 
1711 #[wasmtime_test(wasm_features(tail_call, function_references))]
return_call_ref_to_imported_function(config: &mut Config) -> Result<()>1712 fn return_call_ref_to_imported_function(config: &mut Config) -> Result<()> {
1713     let engine = Engine::new(config)?;
1714 
1715     let module = Module::new(
1716         &engine,
1717         r#"
1718             (module
1719               (type (func (result i32)))
1720               (func (export "run") (param (ref 0)) (result i32)
1721                 (return_call_ref 0 (local.get 0))
1722               )
1723             )
1724         "#,
1725     )?;
1726 
1727     let mut store = Store::new(&engine, ());
1728     let host_func = Func::wrap(&mut store, || -> Result<i32> { bail!("whoopsie") });
1729     let instance = Instance::new(&mut store, &module, &[])?;
1730 
1731     let run = instance.get_typed_func::<Func, i32>(&mut store, "run")?;
1732     let err = run.call(&mut store, host_func).unwrap_err();
1733     err.assert_contains("whoopsie");
1734 
1735     Ok(())
1736 }
1737 
1738 #[wasmtime_test(wasm_features(tail_call, function_references))]
return_call_indirect_to_imported_function(config: &mut Config) -> Result<()>1739 fn return_call_indirect_to_imported_function(config: &mut Config) -> Result<()> {
1740     let engine = Engine::new(config)?;
1741 
1742     let module = Module::new(
1743         &engine,
1744         r#"
1745             (module
1746               (import "" "" (func (result i32)))
1747               (table 1 funcref (ref.func 0))
1748               (func (export "run") (result i32)
1749                 (return_call_indirect (result i32) (i32.const 0))
1750               )
1751             )
1752         "#,
1753     )?;
1754 
1755     let mut store = Store::new(&engine, ());
1756     let host_func = Func::wrap(&mut store, || -> Result<i32> { bail!("whoopsie") });
1757     let instance = Instance::new(&mut store, &module, &[host_func.into()])?;
1758 
1759     let run = instance.get_typed_func::<(), i32>(&mut store, "run")?;
1760     let err = run.call(&mut store, ()).unwrap_err();
1761     err.assert_contains("whoopsie");
1762 
1763     Ok(())
1764 }
1765 
1766 #[test]
return_call_to_aborting_wasm_function_with_stack_adjustments() -> Result<()>1767 fn return_call_to_aborting_wasm_function_with_stack_adjustments() -> Result<()> {
1768     let engine = Engine::default();
1769     let module = Module::new(
1770         &engine,
1771         r#"
1772 (module
1773   (func (export "entry")
1774         (param i64 i64 i64 i64 i64 i64)
1775         (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1776     return_call $abort
1777   )
1778   (func $abort (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1779     unreachable
1780   )
1781 )
1782         "#,
1783     )?;
1784     let mut store = Store::new(&engine, ());
1785     let instance = Instance::new(&mut store, &module, &[])?;
1786     let func = instance.get_func(&mut store, "entry").unwrap();
1787     let args = vec![Val::I64(0); 6];
1788     let mut results = vec![Val::I64(0); 10];
1789 
1790     let err = func.call(&mut store, &args, &mut results).unwrap_err();
1791 
1792     let trap: &Trap = err.downcast_ref().unwrap();
1793     assert_eq!(*trap, Trap::UnreachableCodeReached);
1794 
1795     let trace: &WasmBacktrace = err.downcast_ref().unwrap();
1796     assert_eq!(trace.frames().len(), 1);
1797     assert_eq!(trace.frames()[0].func_name(), Some("abort"));
1798 
1799     let module2 = Module::new(
1800         &engine,
1801         r#"
1802 (module
1803   (func (export "entry")
1804         (param i64 i64 i64 i64 i64 i64)
1805         (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1806     return_call $foo
1807   )
1808   (func $foo (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
1809     call $abort
1810     unreachable
1811   )
1812   (func $abort unreachable)
1813 )
1814         "#,
1815     )?;
1816     let instance = Instance::new(&mut store, &module2, &[])?;
1817     let func = instance.get_func(&mut store, "entry").unwrap();
1818 
1819     let err = func.call(&mut store, &args, &mut results).unwrap_err();
1820 
1821     let trap: &Trap = err.downcast_ref().unwrap();
1822     assert_eq!(*trap, Trap::UnreachableCodeReached);
1823 
1824     let trace: &WasmBacktrace = err.downcast_ref().unwrap();
1825     assert_eq!(trace.frames().len(), 2);
1826     assert_eq!(trace.frames()[0].func_name(), Some("abort"));
1827     assert_eq!(trace.frames()[1].func_name(), Some("foo"));
1828 
1829     Ok(())
1830 }
1831 
1832 #[test]
test_wasm_backtrace_max_frames() -> Result<()>1833 fn test_wasm_backtrace_max_frames() -> Result<()> {
1834     fn run(max_frames: Option<NonZeroUsize>) -> wasmtime::Error {
1835         let wat = r#"
1836             (module
1837                 (func $f1 (export "f1") (call $f2))
1838                 (func $f2 (call $f3))
1839                 (func $f3 (call $f4))
1840                 (func $f4 (call $f5))
1841                 (func $f5 (call $f6))
1842                 (func $f6 (call $f7))
1843                 (func $f7 (call $f8))
1844                 (func $f8 (call $f9))
1845                 (func $f9 (call $f10))
1846                 (func $f10 (unreachable))
1847             )
1848         "#;
1849 
1850         let mut config = Config::new();
1851         config.wasm_backtrace_max_frames(max_frames);
1852         let engine = Engine::new(&config).unwrap();
1853         let module = Module::new(&engine, wat).unwrap();
1854         let mut store = Store::new(&engine, ());
1855         let instance = Instance::new(&mut store, &module, &[]).unwrap();
1856         let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1").unwrap();
1857         f1.call(&mut store, ()).unwrap_err()
1858     }
1859 
1860     // Capturing more than 10 frames should get them all.
1861     let err = run(NonZeroUsize::new(20));
1862     let bt = err.downcast_ref::<WasmBacktrace>().unwrap();
1863     assert_eq!(bt.frames().len(), 10);
1864 
1865     // Limit to 5 frames gets the top 5.
1866     let err = run(NonZeroUsize::new(5));
1867     let bt = err.downcast_ref::<WasmBacktrace>().unwrap();
1868     assert_eq!(bt.frames().len(), 5);
1869 
1870     // Limit of None means no frames collected
1871     let err = run(None);
1872     assert!(err.downcast_ref::<WasmBacktrace>().is_none());
1873 
1874     Ok(())
1875 }
1876