Maximum Call Stack Depth
In Rhai, it is trivial for a function call to perform infinite recursion (or a very deeply-nested recursion) such that all stack space is exhausted.
// This is a function that, when called, recurses forever.
fn recurse_forever() {
recurse_forever();
}
The main stack-size of a program is not determined by Rust but is platform-dependent.
See this on-line Rust docs for more details.
Because of its intended embedded usage, Rhai, by default, limits function calls to a maximum depth of 64 levels (8 levels in debug build) in order to fit into most platforms’ default stack sizes.
This limit may be changed via the Engine::set_max_call_levels
method.
A script exceeding the maximum call stack depth will terminate with an error result.
This check can be disabled via the unchecked
feature for higher performance (but higher risks as well).
let mut engine = Engine::new();
engine.set_max_call_levels(10); // allow only up to 10 levels of function calls
engine.set_max_call_levels(0); // allow no function calls at all (max depth = zero)
When setting this limit, care must be also be taken to the evaluation depth of each statement within a function.
It is entirely possible for a malicious script to embed a recursive call deep inside a nested expression or statements block (see maximum statement depth).
fn bad_function(n) {
// Bail out long before reaching the limit
if n > 10 {
return;
}
// Nest many, many levels deep...
if check_1() {
if check_2() {
if check_3() {
if check_4() {
:
if check_n() {
bad_function(n+1); // <- recursive call!
}
:
}
}
}
}
}
// The function call below may still overflow the stack!
bad_function(0);
While the stack size of a program’s main thread is platform-specific, Rust defaults to a stack size of 2MB for spawned threads.
This default can further be changed such that a spawned thread has as large a stack as needed.
See the on-line Rust docs for more details.
Therefore, in order to relax the stack size limit for scripts, run the Engine
in a separate
spawned thread with a larger stack.