Maximum Expression Nesting Depth
Rhai by default limits statement and expression nesting to a maximum depth of 64 (which should be plenty) when they are at global level, but only a depth of 32 when they are within function bodies.
For debug builds, these limits are set further downwards to 32 and 16 respectively.
That is because it is possible to overflow the Engine
’s stack when it tries to
recursively parse an extremely deeply-nested code stream.
// The following, if long enough, can easily cause stack overflow during parsing.
let a = (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(...)+1)))))))))));
This limit may be changed via Engine::set_max_expr_depths
.
There are two limits to set, one for the maximum depth at global level, and the other for function bodies.
A script exceeding the maximum nesting depths will terminate with a parse error.
The malicious AST
will not be able to get past parsing in the first place.
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_expr_depths(50, 5); // allow nesting up to 50 layers of expressions/statements
// at global level, but only 5 inside functions
Multiple layers of expressions may be generated for a simple language construct, even though it may correspond
to only one AST node.
That is because the Rhai _parser_ internally runs a recursive chain of [function] calls and it is important that
a malicious script does not panic the parser in the first place.
_[Functions]_ are placed under stricter limits because of the multiplicative effect of _recursion_.
A [function] can effectively call itself while deep inside an expression chain within the [function]
body, thereby overflowing the stack even when the level of recursion is within limit.
```rust
fn deep_calc(a, n) {
(a+(a+(a+(a+(a+(a+(a+(a+(a+ ... (a+deep_calc(a,n+1)) ... )))))))))
// ^^^^^^^^^^^^^^^^ recursive call!
}
let a = 42;
let result = (a+(a+(a+(a+(a+(a+(a+(a+(a+ ... (a+deep_calc(a,0)) ... )))))))));
```
In the contrived example above, each recursive call to the [function] `deep_calc` adds the total
number of nested expression layers to Rhai's evaluation stack. Sooner or later (most likely sooner
than the limit for [maximum depth of function calls][maximum call stack depth] is reached), a stack
overflow can be expected.
In general, make sure that `C x ( 5 + F ) + S` layered calls do not cause a stack overflow, where:
* `C` = maximum call stack depth,
* `F` = maximum statement depth for [functions],
* `S` = maximum statement depth at global level.