1 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2 use wasmtime::*;
3 
4 #[test]
5 fn host_always_has_some_stack() -> anyhow::Result<()> {
6     static HITS: AtomicUsize = AtomicUsize::new(0);
7     // assume hosts always have at least 512k of stack
8     const HOST_STACK: usize = 512 * 1024;
9 
10     let mut store = Store::<()>::default();
11 
12     // Create a module that's infinitely recursive, but calls the host on each
13     // level of wasm stack to always test how much host stack we have left.
14     let module = Module::new(
15         store.engine(),
16         r#"
17             (module
18                 (import "" "" (func $host))
19                 (func $recursive (export "foo")
20                     call $host
21                     call $recursive)
22             )
23         "#,
24     )?;
25     let func = Func::wrap(&mut store, test_host_stack);
26     let instance = Instance::new(&mut store, &module, &[func.into()])?;
27     let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
28 
29     // Make sure that our function traps and the trap says that the call stack
30     // has been exhausted.
31     let trap = foo.call(&mut store, ()).unwrap_err();
32     assert!(
33         trap.to_string().contains("call stack exhausted"),
34         "{}",
35         trap.to_string()
36     );
37 
38     // Additionally, however, and this is the crucial test, make sure that the
39     // host function actually completed. If HITS is 1 then we entered but didn't
40     // exit meaning we segfaulted while executing the host, yet still tried to
41     // recover from it with longjmp.
42     assert_eq!(HITS.load(SeqCst), 0);
43 
44     return Ok(());
45 
46     fn test_host_stack() {
47         HITS.fetch_add(1, SeqCst);
48         assert!(consume_some_stack(0, HOST_STACK) > 0);
49         HITS.fetch_sub(1, SeqCst);
50     }
51 
52     #[inline(never)]
53     fn consume_some_stack(ptr: usize, stack: usize) -> usize {
54         if stack == 0 {
55             return ptr;
56         }
57         let mut space = [0u8; 1024];
58         consume_some_stack(space.as_mut_ptr() as usize, stack.saturating_sub(1024))
59     }
60 }
61