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