1c0bb341dSAlex Crichton #![cfg(not(miri))]
2c0bb341dSAlex Crichton 
33862c1f3SAlex Crichton use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
43862c1f3SAlex Crichton use wasmtime::*;
5e32292c9SAlex Crichton use wasmtime_test_macros::wasmtime_test;
63862c1f3SAlex Crichton 
73862c1f3SAlex Crichton #[test]
host_always_has_some_stack() -> Result<()>88bc75502SPeter Huene fn host_always_has_some_stack() -> Result<()> {
93862c1f3SAlex Crichton     static HITS: AtomicUsize = AtomicUsize::new(0);
10aeaca206SAlex Crichton     // assume hosts always have at least 128k of stack
11aeaca206SAlex Crichton     const HOST_STACK: usize = 128 * 1024;
123862c1f3SAlex Crichton 
1313342b24SAlex Crichton     let mut store = if cfg!(target_arch = "x86_64") {
1413342b24SAlex Crichton         let mut config = Config::new();
1513342b24SAlex Crichton         // Force cranelift-based libcalls to show up by ensuring that platform
1613342b24SAlex Crichton         // support is turned off.
1713342b24SAlex Crichton         unsafe {
1813342b24SAlex Crichton             config.cranelift_flag_set("has_avx", "false");
1913342b24SAlex Crichton             config.cranelift_flag_set("has_sse42", "false");
2013342b24SAlex Crichton             config.cranelift_flag_set("has_sse41", "false");
2113342b24SAlex Crichton             config.cranelift_flag_set("has_ssse3", "false");
2213342b24SAlex Crichton             config.cranelift_flag_set("has_sse3", "false");
2313342b24SAlex Crichton         }
2413342b24SAlex Crichton         Store::new(&Engine::new(&config)?, ())
2513342b24SAlex Crichton     } else {
2613342b24SAlex Crichton         Store::<()>::default()
2713342b24SAlex Crichton     };
283862c1f3SAlex Crichton 
293862c1f3SAlex Crichton     // Create a module that's infinitely recursive, but calls the host on each
303862c1f3SAlex Crichton     // level of wasm stack to always test how much host stack we have left.
3113342b24SAlex Crichton     //
3213342b24SAlex Crichton     // Each of the function exports of this module calls out to the host in a
3313342b24SAlex Crichton     // different way, and each one is tested below to make sure that the way of
3413342b24SAlex Crichton     // exiting out to the host is tested thoroughly.
353862c1f3SAlex Crichton     let module = Module::new(
3615c68f2cSYury Delendik         store.engine(),
373862c1f3SAlex Crichton         r#"
383862c1f3SAlex Crichton             (module
3913342b24SAlex Crichton                 (import "" "" (func $host1))
4013342b24SAlex Crichton                 (import "" "" (func $host2))
4113342b24SAlex Crichton 
4213342b24SAlex Crichton                 ;; exit via wasm-to-native trampoline
4313342b24SAlex Crichton                 (func $recursive1 (export "f1")
4413342b24SAlex Crichton                     call $host1
4513342b24SAlex Crichton                     call $recursive1)
4613342b24SAlex Crichton 
4713342b24SAlex Crichton                 ;; exit via wasm-to-array trampoline
4813342b24SAlex Crichton                 (func $recursive2 (export "f2")
4913342b24SAlex Crichton                     call $host2
5013342b24SAlex Crichton                     call $recursive2)
5113342b24SAlex Crichton 
5213342b24SAlex Crichton                 ;; exit via a wasmtime-based libcall
5313342b24SAlex Crichton                 (memory 1)
5413342b24SAlex Crichton                 (func $recursive3 (export "f3")
5513342b24SAlex Crichton                     (drop (memory.grow (i32.const 0)))
5613342b24SAlex Crichton                     call $recursive3)
5713342b24SAlex Crichton 
5813342b24SAlex Crichton                 ;; exit via a cranelift-based libcall
5913342b24SAlex Crichton                 (func $recursive4 (export "f4")
6013342b24SAlex Crichton                     (drop (call $f32_ceil (f32.const 0)))
6113342b24SAlex Crichton                     call $recursive4)
6213342b24SAlex Crichton                 (func $f32_ceil (param f32) (result f32)
6313342b24SAlex Crichton                     (f32.ceil (local.get 0)))
643862c1f3SAlex Crichton             )
653862c1f3SAlex Crichton         "#,
663862c1f3SAlex Crichton     )?;
6713342b24SAlex Crichton     let host1 = Func::wrap(&mut store, test_host_stack);
6813342b24SAlex Crichton     let ty = FuncType::new(store.engine(), [], []);
6913342b24SAlex Crichton     let host2 = Func::new(&mut store, ty, |_, _, _| {
7013342b24SAlex Crichton         test_host_stack();
7113342b24SAlex Crichton         Ok(())
7213342b24SAlex Crichton     });
7313342b24SAlex Crichton     let instance = Instance::new(&mut store, &module, &[host1.into(), host2.into()])?;
7413342b24SAlex Crichton     let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?;
7513342b24SAlex Crichton     let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?;
7613342b24SAlex Crichton     let f3 = instance.get_typed_func::<(), ()>(&mut store, "f3")?;
7713342b24SAlex Crichton     let f4 = instance.get_typed_func::<(), ()>(&mut store, "f4")?;
783862c1f3SAlex Crichton 
793862c1f3SAlex Crichton     // Make sure that our function traps and the trap says that the call stack
803862c1f3SAlex Crichton     // has been exhausted.
8113342b24SAlex Crichton     let hits1 = HITS.load(SeqCst);
8213342b24SAlex Crichton     let trap = f1.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
832afaac51SAlex Crichton     assert_eq!(trap, Trap::StackOverflow);
8413342b24SAlex Crichton     let hits2 = HITS.load(SeqCst);
8513342b24SAlex Crichton     let trap = f2.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
8613342b24SAlex Crichton     assert_eq!(trap, Trap::StackOverflow);
8713342b24SAlex Crichton     let hits3 = HITS.load(SeqCst);
8813342b24SAlex Crichton     let trap = f3.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
8913342b24SAlex Crichton     assert_eq!(trap, Trap::StackOverflow);
9013342b24SAlex Crichton     let hits4 = HITS.load(SeqCst);
9113342b24SAlex Crichton     let trap = f4.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
9213342b24SAlex Crichton     assert_eq!(trap, Trap::StackOverflow);
9313342b24SAlex Crichton     let hits5 = HITS.load(SeqCst);
943862c1f3SAlex Crichton 
953862c1f3SAlex Crichton     // Additionally, however, and this is the crucial test, make sure that the
963862c1f3SAlex Crichton     // host function actually completed. If HITS is 1 then we entered but didn't
973862c1f3SAlex Crichton     // exit meaning we segfaulted while executing the host, yet still tried to
98192f2fcdSAlex Crichton     // recover from it with a jump.
9913342b24SAlex Crichton     assert_eq!(hits1, 0);
10013342b24SAlex Crichton     assert_eq!(hits2, 0);
10113342b24SAlex Crichton     assert_eq!(hits3, 0);
10213342b24SAlex Crichton     assert_eq!(hits4, 0);
10313342b24SAlex Crichton     assert_eq!(hits5, 0);
1043862c1f3SAlex Crichton 
1053862c1f3SAlex Crichton     return Ok(());
1063862c1f3SAlex Crichton 
1073862c1f3SAlex Crichton     fn test_host_stack() {
1083862c1f3SAlex Crichton         HITS.fetch_add(1, SeqCst);
1093862c1f3SAlex Crichton         assert!(consume_some_stack(0, HOST_STACK) > 0);
1103862c1f3SAlex Crichton         HITS.fetch_sub(1, SeqCst);
1113862c1f3SAlex Crichton     }
1123862c1f3SAlex Crichton 
1133862c1f3SAlex Crichton     #[inline(never)]
1143862c1f3SAlex Crichton     fn consume_some_stack(ptr: usize, stack: usize) -> usize {
1153862c1f3SAlex Crichton         if stack == 0 {
1163862c1f3SAlex Crichton             return ptr;
1173862c1f3SAlex Crichton         }
1183862c1f3SAlex Crichton         let mut space = [0u8; 1024];
1193862c1f3SAlex Crichton         consume_some_stack(space.as_mut_ptr() as usize, stack.saturating_sub(1024))
1203862c1f3SAlex Crichton     }
1213862c1f3SAlex Crichton }
1228bc75502SPeter Huene 
123e32292c9SAlex Crichton #[wasmtime_test]
big_stack_works_ok(config: &mut Config) -> Result<()>124e32292c9SAlex Crichton fn big_stack_works_ok(config: &mut Config) -> Result<()> {
12547bc186dSAlex Crichton     // This test takes 1m+ in ASAN and isn't too useful, so prune it.
12647bc186dSAlex Crichton     if cfg!(asan) {
12747bc186dSAlex Crichton         return Ok(());
12847bc186dSAlex Crichton     }
12947bc186dSAlex Crichton 
1308bc75502SPeter Huene     const N: usize = 10000;
1318bc75502SPeter Huene 
1328bc75502SPeter Huene     // Build a module with a function that uses a very large amount of stack space,
1338bc75502SPeter Huene     // modeled here by calling an i64-returning-function many times followed by
1348bc75502SPeter Huene     // adding them all into one i64.
1358bc75502SPeter Huene     //
1368bc75502SPeter Huene     // This should exercise the ability to consume multi-page stacks and
1378bc75502SPeter Huene     // only touch a few internals of it at a time.
1388bc75502SPeter Huene     let mut s = String::new();
1398bc75502SPeter Huene     s.push_str("(module\n");
1408bc75502SPeter Huene     s.push_str("(func (export \"\") (result i64)\n");
1418bc75502SPeter Huene     s.push_str("i64.const 0\n");
1428bc75502SPeter Huene     for _ in 0..N {
1438bc75502SPeter Huene         s.push_str("call $get\n");
1448bc75502SPeter Huene     }
1458bc75502SPeter Huene     for _ in 0..N {
1468bc75502SPeter Huene         s.push_str("i64.add\n");
1478bc75502SPeter Huene     }
1488bc75502SPeter Huene     s.push_str(")\n");
1498bc75502SPeter Huene     s.push_str("(func $get (result i64) i64.const 0)\n");
1508bc75502SPeter Huene     s.push_str(")\n");
1518bc75502SPeter Huene 
152e32292c9SAlex Crichton     // Disable cranelift optimizations to ensure that this test doesn't take too
153e32292c9SAlex Crichton     // long in debug mode due to the large size of its code.
154e32292c9SAlex Crichton     config.cranelift_opt_level(OptLevel::None);
15545f8b2a7SAlex Crichton     config.cranelift_regalloc_algorithm(RegallocAlgorithm::SinglePass);
156e32292c9SAlex Crichton     let engine = Engine::new(config)?;
157e32292c9SAlex Crichton 
158e32292c9SAlex Crichton     let mut store = Store::new(&engine, ());
1598bc75502SPeter Huene     let module = Module::new(store.engine(), &s)?;
1608bc75502SPeter Huene     let instance = Instance::new(&mut store, &module, &[])?;
1618bc75502SPeter Huene     let func = instance.get_typed_func::<(), i64>(&mut store, "")?;
1628bc75502SPeter Huene     assert_eq!(func.call(&mut store, ())?, 0);
1638bc75502SPeter Huene     Ok(())
1648bc75502SPeter Huene }
165*2283e84fSAlex Crichton 
166*2283e84fSAlex Crichton #[test]
infinite_wasm_stack_does_not_panic() -> Result<()>167*2283e84fSAlex Crichton fn infinite_wasm_stack_does_not_panic() -> Result<()> {
168*2283e84fSAlex Crichton     let mut config = Config::new();
169*2283e84fSAlex Crichton     config.max_wasm_stack(usize::MAX);
170*2283e84fSAlex Crichton     config.async_stack_size(usize::MAX);
171*2283e84fSAlex Crichton     let engine = Engine::new(&config)?;
172*2283e84fSAlex Crichton     // If the store can't get allocated that's probably because we're using
173*2283e84fSAlex Crichton     // pulley and can't allocate a usize::MAX stack, ignore that failure.
174*2283e84fSAlex Crichton     // This'll still run on cranelift-native platforms.
175*2283e84fSAlex Crichton     let Ok(mut store) = Store::try_new(&engine, ()) else {
176*2283e84fSAlex Crichton         return Ok(());
177*2283e84fSAlex Crichton     };
178*2283e84fSAlex Crichton     let module = Module::new(store.engine(), r#"(module (func (export "f")))"#)?;
179*2283e84fSAlex Crichton     let instance = Instance::new(&mut store, &module, &[])?;
180*2283e84fSAlex Crichton     let func = instance.get_typed_func::<(), ()>(&mut store, "f")?;
181*2283e84fSAlex Crichton     func.call(&mut store, ())?;
182*2283e84fSAlex Crichton     Ok(())
183*2283e84fSAlex Crichton }
184