Use u64 for operations counter.

This commit is contained in:
Stephen Chung 2020-05-19 10:08:27 +08:00
parent 1824dced69
commit 6b8c6bda42
4 changed files with 31 additions and 36 deletions

View File

@ -2347,7 +2347,7 @@ engine.set_max_modules(0); // allow unlimited modules
### Maximum call stack depth ### Maximum call stack depth
Rhai by default limits function calls to a maximum depth of 128 levels (8 levels in debug build). Rhai by default limits function calls to a maximum depth of 128 levels (16 levels in debug build).
This limit may be changed via the `Engine::set_max_call_levels` method. This limit may be changed via the `Engine::set_max_call_levels` method.
When setting this limit, care must be also taken to the evaluation depth of each _statement_ When setting this limit, care must be also taken to the evaluation depth of each _statement_

View File

@ -27,7 +27,7 @@ use crate::stdlib::{
format, format,
iter::{empty, once, repeat}, iter::{empty, once, repeat},
mem, mem,
num::{NonZeroU64, NonZeroUsize}, num::NonZeroUsize,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
@ -49,7 +49,7 @@ pub type Map = HashMap<String, Dynamic>;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 8; pub const MAX_CALL_STACK_DEPTH: usize = 16;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_EXPR_DEPTH: usize = 32; pub const MAX_EXPR_DEPTH: usize = 32;
@ -354,16 +354,16 @@ pub struct Engine {
pub(crate) optimization_level: OptimizationLevel, pub(crate) optimization_level: OptimizationLevel,
/// Maximum levels of call-stack to prevent infinite recursion. /// Maximum levels of call-stack to prevent infinite recursion.
/// ///
/// Defaults to 8 for debug builds and 128 for non-debug builds. /// Defaults to 16 for debug builds and 128 for non-debug builds.
pub(crate) max_call_stack_depth: usize, pub(crate) max_call_stack_depth: usize,
/// Maximum depth of statements/expressions at global level. /// Maximum depth of statements/expressions at global level.
pub(crate) max_expr_depth: usize, pub(crate) max_expr_depth: usize,
/// Maximum depth of statements/expressions in functions. /// Maximum depth of statements/expressions in functions.
pub(crate) max_function_expr_depth: usize, pub(crate) max_function_expr_depth: usize,
/// Maximum number of operations allowed to run. /// Maximum number of operations allowed to run.
pub(crate) max_operations: Option<NonZeroU64>, pub(crate) max_operations: u64,
/// Maximum number of modules allowed to load. /// Maximum number of modules allowed to load.
pub(crate) max_modules: Option<NonZeroU64>, pub(crate) max_modules: u64,
} }
impl Default for Engine { impl Default for Engine {
@ -404,8 +404,8 @@ impl Default for Engine {
max_call_stack_depth: MAX_CALL_STACK_DEPTH, max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH, max_expr_depth: MAX_EXPR_DEPTH,
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_operations: None, max_operations: u64::MAX,
max_modules: None, max_modules: u64::MAX,
}; };
#[cfg(feature = "no_stdlib")] #[cfg(feature = "no_stdlib")]
@ -547,8 +547,8 @@ impl Engine {
max_call_stack_depth: MAX_CALL_STACK_DEPTH, max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH, max_expr_depth: MAX_EXPR_DEPTH,
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH, max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_operations: None, max_operations: u64::MAX,
max_modules: None, max_modules: u64::MAX,
} }
} }
@ -589,13 +589,17 @@ impl Engine {
/// consuming too much resources (0 for unlimited). /// consuming too much resources (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_operations(&mut self, operations: u64) { pub fn set_max_operations(&mut self, operations: u64) {
self.max_operations = NonZeroU64::new(operations); self.max_operations = if operations == 0 {
u64::MAX
} else {
operations
};
} }
/// Set the maximum number of imported modules allowed for a script (0 for unlimited). /// Set the maximum number of imported modules allowed for a script (0 for unlimited).
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
pub fn set_max_modules(&mut self, modules: u64) { pub fn set_max_modules(&mut self, modules: u64) {
self.max_modules = NonZeroU64::new(modules); self.max_modules = if modules == 0 { u64::MAX } else { modules };
} }
/// Set the depth limits for expressions/statements. /// Set the depth limits for expressions/statements.
@ -1835,11 +1839,9 @@ impl Engine {
let (expr, (name, pos)) = x.as_ref(); let (expr, (name, pos)) = x.as_ref();
// Guard against too many modules // Guard against too many modules
if let Some(max) = self.max_modules { if state.modules >= self.max_modules {
if state.modules >= max.get() {
return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos))); return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos)));
} }
}
if let Some(path) = self if let Some(path) = self
.eval_expr(scope, state, &expr, level)? .eval_expr(scope, state, &expr, level)?
@ -1902,12 +1904,10 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
// Guard against too many operations // Guard against too many operations
if let Some(max) = self.max_operations { if state.operations > self.max_operations {
if state.operations > max.get() {
return Err(Box::new(EvalAltResult::ErrorTooManyOperations(pos))); return Err(Box::new(EvalAltResult::ErrorTooManyOperations(pos)));
} }
} }
}
// Report progress - only in steps // Report progress - only in steps
if let Some(progress) = &self.progress { if let Some(progress) = &self.progress {

View File

@ -86,14 +86,14 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>( .eval::<INT>(
r#" r#"
let x = 0; let sum = 0;
for x in range(0, 10) { for x in range(0, 10) {
import "hello" as h; import "hello" as h;
x += h::answer; sum += h::answer;
} }
x sum
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
@ -105,18 +105,18 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>( .eval::<INT>(
r#" r#"
let x = 0; let sum = 0;
fn foo() { fn foo() {
import "hello" as h; import "hello" as h;
x += h::answer; sum += h::answer;
} }
for x in range(0, 10) { for x in range(0, 10) {
foo(); foo();
} }
x sum
"# "#
) )
.expect_err("should error"), .expect_err("should error"),

View File

@ -16,19 +16,14 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box<EvalAltResult>> {
); );
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
match engine.eval::<()>( assert!(matches!(
*engine.eval::<()>(
r" r"
fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } } fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } }
foo(1000) foo(1000)
", ").expect_err("should error"),
) { EvalAltResult::ErrorInFunctionCall(name, _, _) if name.starts_with("foo > foo > foo")
Ok(_) => panic!("should be stack overflow"), ));
Err(err) => match *err {
EvalAltResult::ErrorInFunctionCall(name, _, _)
if name.starts_with("foo > foo > foo") => {}
_ => panic!("should be stack overflow"),
},
}
Ok(()) Ok(())
} }