Use NonZero for limits.

This commit is contained in:
Stephen Chung 2021-01-06 13:46:53 +08:00
parent 3fbcefe0ed
commit a5d6392107
4 changed files with 119 additions and 112 deletions

View File

@ -135,7 +135,8 @@ impl Imports {
.rev()
.find_map(|(_, m)| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
}
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]?
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of
/// imported [modules][Module]?
#[allow(dead_code)]
#[inline(always)]
pub fn contains_iter(&self, id: TypeId) -> bool {
@ -508,8 +509,8 @@ pub struct State {
/// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned.
/// When that happens, this flag is turned on to force a scope lookup by name.
pub always_search: bool,
/// Level of the current scope. The global (root) level is zero, a new block (or function call)
/// is one level higher, and so on.
/// Level of the current scope. The global (root) level is zero, a new block
/// (or function call) is one level higher, and so on.
pub scope_level: usize,
/// Number of operations performed.
pub operations: u64,
@ -542,30 +543,34 @@ impl State {
pub struct Limits {
/// Maximum levels of call-stack to prevent infinite recursion.
/// Not available under `no_function`.
///
/// Set to zero to effectively disable function calls.
#[cfg(not(feature = "no_function"))]
pub max_call_stack_depth: usize,
/// Maximum depth of statements/expressions at global level (0 = unlimited).
pub max_expr_depth: usize,
/// Maximum depth of statements/expressions in functions (0 = unlimited).
/// Maximum depth of statements/expressions at global level.
pub max_expr_depth: Option<NonZeroUsize>,
/// Maximum depth of statements/expressions in functions.
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_function_expr_depth: usize,
/// Maximum number of operations allowed to run (0 = unlimited).
pub max_operations: u64,
pub max_function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<NonZeroU64>,
/// Maximum number of [modules][Module] allowed to load.
/// Not available under `no_module`.
///
/// Set to zero to effectively disable loading any [module][Module].
#[cfg(not(feature = "no_module"))]
pub max_modules: usize,
/// Maximum length of a [string][ImmutableString] (0 = unlimited).
pub max_string_size: usize,
/// Maximum length of an [array][Array] (0 = unlimited).
/// Maximum length of a [string][ImmutableString].
pub max_string_size: Option<NonZeroUsize>,
/// Maximum length of an [array][Array].
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
pub max_array_size: usize,
/// Maximum number of properties in an [object map][Map] (0 = unlimited).
pub max_array_size: Option<NonZeroUsize>,
/// Maximum number of properties in an [object map][Map].
/// Not available under `no_object`.
#[cfg(not(feature = "no_object"))]
pub max_map_size: usize,
pub max_map_size: Option<NonZeroUsize>,
}
/// Context of a script evaluation process.
@ -777,13 +782,13 @@ pub fn search_imports(
// Qualified - check if the root module is directly indexed
let index = if state.always_search {
0
None
} else {
namespace.index().map_or(0, NonZeroUsize::get)
namespace.index()
};
Ok(if index > 0 {
let offset = mods.len() - index;
Ok(if let Some(index) = index {
let offset = mods.len() - index.get();
mods.get(offset).expect("invalid index in Imports")
} else {
mods.find(root)
@ -838,17 +843,17 @@ impl Engine {
limits: Limits {
#[cfg(not(feature = "no_function"))]
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH,
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_operations: 0,
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_size: 0,
max_string_size: None,
#[cfg(not(feature = "no_index"))]
max_array_size: 0,
max_array_size: None,
#[cfg(not(feature = "no_object"))]
max_map_size: 0,
max_map_size: None,
},
disable_doc_comments: false,
@ -895,17 +900,17 @@ impl Engine {
limits: Limits {
#[cfg(not(feature = "no_function"))]
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
max_expr_depth: MAX_EXPR_DEPTH,
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: MAX_FUNCTION_EXPR_DEPTH,
max_operations: 0,
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_size: 0,
max_string_size: None,
#[cfg(not(feature = "no_index"))]
max_array_size: 0,
max_array_size: None,
#[cfg(not(feature = "no_object"))]
max_map_size: 0,
max_map_size: None,
},
disable_doc_comments: false,
@ -975,14 +980,11 @@ impl Engine {
}
// Check if it is directly indexed
let index = if state.always_search {
0
} else {
index.map_or(0, NonZeroUsize::get)
};
let index = if state.always_search { &None } else { index };
// Check the variable resolver, if any
if let Some(ref resolve_var) = self.resolve_var {
let index = index.map(NonZeroUsize::get).unwrap_or(0);
let context = EvalContext {
engine: self,
scope,
@ -1000,8 +1002,8 @@ impl Engine {
}
}
let index = if index > 0 {
scope.len() - index
let index = if let Some(index) = index {
scope.len() - index.get()
} else {
// Find the variable in the scope
scope
@ -1012,8 +1014,8 @@ impl Engine {
let val = scope.get_mut_by_index(index);
// Check for data race - probably not necessary because the only place it should conflict is in a method call
// when the object variable is also used as a parameter.
// Check for data race - probably not necessary because the only place it should conflict is
// in a method call when the object variable is also used as a parameter.
// if cfg!(not(feature = "no_closure")) && val.is_locked() {
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
// }
@ -1285,7 +1287,8 @@ impl Engine {
)
.or_else(
|err| match *err {
// If there is no setter, no need to feed it back because the property is read-only
// If there is no setter, no need to feed it back because
// the property is read-only
EvalAltResult::ErrorDotExpr(_, _) => {
Ok((Dynamic::UNIT, false))
}
@ -1405,7 +1408,8 @@ impl Engine {
}
/// Evaluate a chain of indexes and store the results in a [`StaticVec`].
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of just a few levels of indexing.
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of
/// just a few levels of indexing.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn eval_indexed_chain(
&self,
@ -2400,20 +2404,23 @@ impl Engine {
result: Result<Dynamic, Box<EvalAltResult>>,
pos: Position,
) -> Result<Dynamic, Box<EvalAltResult>> {
// If no data size limits, just return
let mut total = 0;
// Simply return all errors
if result.is_err() {
return result;
}
total += self.max_string_size();
// If no data size limits, just return
let mut has_limit = self.limits.max_string_size.is_some();
#[cfg(not(feature = "no_index"))]
{
total += self.max_array_size();
has_limit = has_limit || self.limits.max_array_size.is_some();
}
#[cfg(not(feature = "no_object"))]
{
total += self.max_map_size();
has_limit = has_limit || self.limits.max_map_size.is_some();
}
if total == 0 {
if !has_limit {
return result;
}
@ -2469,34 +2476,33 @@ impl Engine {
}
}
match result {
// Simply return all errors
Err(_) => return result,
// String with limit
Ok(Dynamic(Union::Str(_, _))) if self.max_string_size() > 0 => (),
// Array with limit
#[cfg(not(feature = "no_index"))]
Ok(Dynamic(Union::Array(_, _))) if self.max_array_size() > 0 => (),
// Map with limit
#[cfg(not(feature = "no_object"))]
Ok(Dynamic(Union::Map(_, _))) if self.max_map_size() > 0 => (),
// Everything else is simply returned
Ok(_) => return result,
};
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
if s > self.max_string_size() {
if s > self
.limits
.max_string_size
.map_or(usize::MAX, NonZeroUsize::get)
{
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), pos).into();
}
#[cfg(not(feature = "no_index"))]
if _arr > self.max_array_size() {
if _arr
> self
.limits
.max_array_size
.map_or(usize::MAX, NonZeroUsize::get)
{
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), pos).into();
}
#[cfg(not(feature = "no_object"))]
if _map > self.max_map_size() {
if _map
> self
.limits
.max_map_size
.map_or(usize::MAX, NonZeroUsize::get)
{
return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), pos).into();
}

View File

@ -1,6 +1,10 @@
//! Configuration settings for [`Engine`].
use crate::stdlib::{format, num::NonZeroU8, string::String};
use crate::stdlib::{
format,
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
string::String,
};
use crate::token::Token;
use crate::Engine;
@ -62,11 +66,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
self.limits.max_operations = if operations == u64::MAX {
0
} else {
operations
};
self.limits.max_operations = NonZeroU64::new(operations);
self
}
/// The maximum number of operations allowed for a script to run (0 for unlimited).
@ -75,7 +75,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_operations(&self) -> u64 {
self.limits.max_operations
self.limits.max_operations.map_or(0, NonZeroU64::get)
}
/// Set the maximum number of imported [modules][crate::Module] allowed for a script.
///
@ -106,18 +106,10 @@ impl Engine {
max_expr_depth: usize,
#[cfg(not(feature = "no_function"))] max_function_expr_depth: usize,
) -> &mut Self {
self.limits.max_expr_depth = if max_expr_depth == usize::MAX {
0
} else {
max_expr_depth
};
self.limits.max_expr_depth = NonZeroUsize::new(max_expr_depth);
#[cfg(not(feature = "no_function"))]
{
self.limits.max_function_expr_depth = if max_function_expr_depth == usize::MAX {
0
} else {
max_function_expr_depth
};
self.limits.max_function_expr_depth = NonZeroUsize::new(max_function_expr_depth);
}
self
}
@ -127,7 +119,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_expr_depth(&self) -> usize {
self.limits.max_expr_depth
self.limits.max_expr_depth.map_or(0, NonZeroUsize::get)
}
/// The depth limit for expressions in functions (0 for unlimited).
///
@ -136,7 +128,9 @@ impl Engine {
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn max_function_expr_depth(&self) -> usize {
self.limits.max_function_expr_depth
self.limits
.max_function_expr_depth
.map_or(0, NonZeroUsize::get)
}
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
///
@ -144,7 +138,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
self.limits.max_string_size = if max_size == usize::MAX { 0 } else { max_size };
self.limits.max_string_size = NonZeroUsize::new(max_size);
self
}
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
@ -153,7 +147,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
#[inline(always)]
pub fn max_string_size(&self) -> usize {
self.limits.max_string_size
self.limits.max_string_size.map_or(0, NonZeroUsize::get)
}
/// Set the maximum length of [arrays][crate::Array] (0 for unlimited).
///
@ -162,7 +156,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn set_max_array_size(&mut self, max_size: usize) -> &mut Self {
self.limits.max_array_size = if max_size == usize::MAX { 0 } else { max_size };
self.limits.max_array_size = NonZeroUsize::new(max_size);
self
}
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
@ -172,7 +166,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
#[inline(always)]
pub fn max_array_size(&self) -> usize {
self.limits.max_array_size
self.limits.max_array_size.map_or(0, NonZeroUsize::get)
}
/// Set the maximum size of [object maps][crate::Map] (0 for unlimited).
///
@ -181,7 +175,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn set_max_map_size(&mut self, max_size: usize) -> &mut Self {
self.limits.max_map_size = if max_size == usize::MAX { 0 } else { max_size };
self.limits.max_map_size = NonZeroUsize::new(max_size);
self
}
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
@ -191,7 +185,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn max_map_size(&self) -> usize {
self.limits.max_map_size
self.limits.max_map_size.map_or(0, NonZeroUsize::get)
}
/// Set the module resolution service used by the [`Engine`].
///

View File

@ -61,11 +61,11 @@ struct ParseState<'e> {
modules: StaticVec<ImmutableString>,
/// Maximum levels of expression nesting.
#[cfg(not(feature = "unchecked"))]
max_expr_depth: usize,
max_expr_depth: Option<NonZeroUsize>,
/// Maximum levels of expression nesting in functions.
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: usize,
max_function_expr_depth: Option<NonZeroUsize>,
}
impl<'e> ParseState<'e> {
@ -73,10 +73,10 @@ impl<'e> ParseState<'e> {
#[inline(always)]
pub fn new(
engine: &'e Engine,
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
#[cfg(not(feature = "unchecked"))] max_expr_depth: Option<NonZeroUsize>,
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: usize,
max_function_expr_depth: Option<NonZeroUsize>,
) -> Self {
Self {
engine,
@ -212,15 +212,17 @@ impl ParseSettings {
}
/// Make sure that the current level of expression nesting is within the maximum limit.
#[cfg(not(feature = "unchecked"))]
#[inline]
pub fn ensure_level_within_max_limit(&self, limit: usize) -> Result<(), ParseError> {
if limit == 0 {
Ok(())
} else if self.level > limit {
Err(PERR::ExprTooDeep.into_err(self.pos))
} else {
Ok(())
#[inline(always)]
pub fn ensure_level_within_max_limit(
&self,
limit: Option<NonZeroUsize>,
) -> Result<(), ParseError> {
if let Some(limit) = limit {
if self.level > limit.get() {
return Err(PERR::ExprTooDeep.into_err(self.pos));
}
}
Ok(())
}
}
@ -3040,10 +3042,10 @@ impl Engine {
let mut state = ParseState::new(
self,
#[cfg(not(feature = "unchecked"))]
self.max_expr_depth(),
NonZeroUsize::new(self.max_expr_depth()),
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
self.max_function_expr_depth(),
NonZeroUsize::new(self.max_function_expr_depth()),
);
let settings = ParseSettings {
@ -3087,10 +3089,10 @@ impl Engine {
let mut state = ParseState::new(
self,
#[cfg(not(feature = "unchecked"))]
self.max_expr_depth(),
NonZeroUsize::new(self.max_expr_depth()),
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
self.max_function_expr_depth(),
NonZeroUsize::new(self.max_function_expr_depth()),
);
while !input.peek().unwrap().0.is_eof() {

View File

@ -8,6 +8,7 @@ use crate::stdlib::{
borrow::Cow,
char, fmt, format,
iter::Peekable,
num::NonZeroUsize,
str::{Chars, FromStr},
string::{String, ToString},
};
@ -747,7 +748,7 @@ impl From<Token> for String {
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct TokenizeState {
/// Maximum length of a string (0 = unlimited).
pub max_string_size: usize,
pub max_string_size: Option<NonZeroUsize>,
/// Can the next token be a unary operator?
pub non_unary: bool,
/// Is the tokenizer currently inside a block comment?
@ -796,8 +797,10 @@ pub fn parse_string_literal(
pos.advance();
if state.max_string_size > 0 && result.len() > state.max_string_size {
return Err((LexError::StringTooLong(state.max_string_size), *pos));
if let Some(max) = state.max_string_size {
if result.len() > max.get() {
return Err((LexError::StringTooLong(max.get()), *pos));
}
}
match next_char {
@ -902,8 +905,10 @@ pub fn parse_string_literal(
let s = result.iter().collect::<String>();
if state.max_string_size > 0 && s.len() > state.max_string_size {
return Err((LexError::StringTooLong(state.max_string_size), *pos));
if let Some(max) = state.max_string_size {
if s.len() > max.get() {
return Err((LexError::StringTooLong(max.get()), *pos));
}
}
Ok(s)