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