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