72 lines
2.8 KiB
Markdown
72 lines
2.8 KiB
Markdown
Maximum Expression Nesting Depth
|
|
================================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
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.
|
|
|
|
```rust
|
|
// 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`][options].
|
|
|
|
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).
|
|
|
|
```rust
|
|
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
|
|
```
|
|
|
|
```admonish warning
|
|
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.
|
|
```
|
|
|
|
~~~admonish danger "Beware of recursion"
|
|
|
|
_[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.
|
|
~~~
|