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