diff --git a/src/engine.rs b/src/engine.rs index e0ce5c43..e24b2ee8 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -49,7 +49,12 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box> + Send + #[cfg(not(feature = "sync"))] type IteratorFn = dyn Fn(&Dynamic) -> Box>; -pub const MAX_CALL_STACK_DEPTH: usize = 64; +#[cfg(debug_assertions)] +pub const MAX_CALL_STACK_DEPTH: usize = 42; + +#[cfg(not(debug_assertions))] +pub const MAX_CALL_STACK_DEPTH: usize = 256; + pub const KEYWORD_PRINT: &str = "print"; pub const KEYWORD_DEBUG: &str = "debug"; pub const KEYWORD_DUMP_AST: &str = "dump_ast"; @@ -271,6 +276,8 @@ pub struct Engine<'e> { pub(crate) optimization_level: OptimizationLevel, /// Maximum levels of call-stack to prevent infinite recursion. + /// + /// Defaults to 42 for debug builds and 256 for non-debug builds. pub(crate) max_call_stack_depth: usize, } @@ -436,6 +443,11 @@ impl Engine<'_> { pos: Position, level: usize, ) -> Result { + // Check for stack overflow + if level > self.max_call_stack_depth { + return Err(EvalAltResult::ErrorStackOverflow(pos)); + } + // First search in script-defined functions (can override built-in) if let Some(lib) = fn_lib { if let Some(fn_def) = lib.get_function(fn_name, args.len()) { diff --git a/tests/stack.rs b/tests/stack.rs new file mode 100644 index 00000000..62964c9c --- /dev/null +++ b/tests/stack.rs @@ -0,0 +1,29 @@ +use rhai::{Engine, EvalAltResult}; + +#[test] +fn test_stack_overflow() -> Result<(), EvalAltResult> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + r" + fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } } + foo(38) + ", + )?, + 741 + ); + + match engine.eval::<()>( + r" + fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } } + foo(1000) + ", + ) { + Ok(_) => panic!("should be stack overflow"), + Err(EvalAltResult::ErrorStackOverflow(_)) => (), + Err(_) => panic!("should be stack overflow"), + } + + Ok(()) +}