Skip to content

Commit

Permalink
Merge pull request #741 from schungx/master
Browse files Browse the repository at this point in the history
Bug fixes.
  • Loading branch information
schungx authored Jul 10, 2023
2 parents ca18cdd + a3c2fb0 commit ed5bbf0
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Bug fixes
---------

* Fixes a panic when using `this` as the first parameter in a namespace-qualified function call.
* `max` and `min` for integers, strings and characters were missing from the standard library. They are now added.

New features
------------

* Added `Engine::max_variables` and `Engine::set_max_variables` to limit the maximum number of variables allowed within a scope at any time. This is to guard against defining a huge number of variables containing large data just beyond individual data size limits. When `max_variables` is exceeded a new error, `ErrorTooManyVariables`, is returned.


Version 1.15.1
Expand Down
21 changes: 19 additions & 2 deletions src/api/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub struct Limits {
pub max_function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<NonZeroU64>,
/// Maximum number of variables allowed at any instant.
///
/// Set to zero to effectively disable creating variables.
pub max_variables: usize,
/// Maximum number of [modules][crate::Module] allowed to load.
///
/// Set to zero to effectively disable loading any [module][crate::Module].
Expand Down Expand Up @@ -78,6 +82,7 @@ impl Limits {
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: NonZeroUsize::new(default_limits::MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
max_variables: usize::MAX,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_len: None,
Expand Down Expand Up @@ -150,8 +155,6 @@ impl Engine {
self
}
/// The maximum number of operations allowed for a script to run (0 for unlimited).
///
/// Not available under `unchecked`.
#[inline]
#[must_use]
pub const fn max_operations(&self) -> u64 {
Expand All @@ -160,6 +163,20 @@ impl Engine {
None => 0,
}
}
/// Set the maximum number of imported variables allowed for a script at any instant.
///
/// Not available under `unchecked`.
#[inline(always)]
pub fn set_max_variables(&mut self, modules: usize) -> &mut Self {
self.limits.max_variables = modules;
self
}
/// The maximum number of imported variables allowed for a script at any instant.
#[inline(always)]
#[must_use]
pub const fn max_variables(&self) -> usize {
self.limits.max_variables
}
/// Set the maximum number of imported [modules][crate::Module] allowed for a script.
///
/// Not available under `unchecked` or `no_module`.
Expand Down
8 changes: 8 additions & 0 deletions src/api/limits_unchecked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ impl Engine {
pub const fn max_operations(&self) -> u64 {
0
}
/// The maximum number of variables allowed for a script at any instant.
///
/// Always returns [`usize::MAX`].
#[inline(always)]
#[must_use]
pub const fn max_variables(&self) -> usize {
usize::MAX
}
/// The maximum number of imported [modules][crate::Module] allowed for a script.
///
/// Always returns [`usize::MAX`].
Expand Down
5 changes: 4 additions & 1 deletion src/eval/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ impl Engine {
if let Some(index) = index {
value.set_access_mode(access);
*scope.get_mut_by_index(scope.len() - index.get()) = value;
} else if !cfg!(feature = "unchecked") && scope.len() >= self.max_variables() {
// Guard against too many variables
return Err(ERR::ErrorTooManyVariables(*pos).into());
} else {
scope.push_entry(var_name.name.clone(), access, value);
}
Expand Down Expand Up @@ -879,7 +882,7 @@ impl Engine {
let (expr, export) = &**x;

// Guard against too many modules
if global.num_modules_loaded >= self.max_modules() {
if !cfg!(feature = "unchecked") && global.num_modules_loaded >= self.max_modules() {
return Err(ERR::ErrorTooManyModules(*_pos).into());
}

Expand Down
6 changes: 6 additions & 0 deletions src/func/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ impl Engine {
.as_ref()
.map_or(0, |dbg| dbg.call_stack().len());

// Guard against too many variables
if !cfg!(feature = "unchecked") && scope.len() + fn_def.params.len() > self.max_variables()
{
return Err(ERR::ErrorTooManyVariables(pos).into());
}

// Put arguments into scope as variables
scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| {
// Actually consume the arguments instead of cloning them
Expand Down
37 changes: 36 additions & 1 deletion src/packages/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ def_package! {
reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64);

#[cfg(not(target_family = "wasm"))]

reg_functions!(lib += num_128; i128, u128);
}

Expand All @@ -73,6 +72,8 @@ def_package! {
combine_with_exported_module!(lib, "decimal", decimal_functions);

combine_with_exported_module!(lib, "logic", logic_functions);

combine_with_exported_module!(lib, "min_max", min_max_functions);
}
}

Expand Down Expand Up @@ -102,6 +103,40 @@ mod logic_functions {
}
}

#[export_module]
mod min_max_functions {
use crate::INT;

/// Return the number that is larger than the other number.
///
/// # Example
///
/// ```rhai
/// max(42, 123); // returns 132
/// ```
pub fn max(x: INT, y: INT) -> INT {
if x >= y {
x
} else {
y
}
}
/// Return the number that is smaller than the other number.
///
/// # Example
///
/// ```rhai
/// min(42, 123); // returns 42
/// ```
pub fn min(x: INT, y: INT) -> INT {
if x <= y {
x
} else {
y
}
}
}

#[cfg(not(feature = "no_float"))]
#[allow(clippy::cast_precision_loss)]
#[export_module]
Expand Down
60 changes: 60 additions & 0 deletions src/packages/string_more.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,66 @@ mod string_functions {

Ok(())
}
/// Return the string that is lexically greater than the other string.
///
/// # Example
///
/// ```rhai
/// max("hello", "world"); // returns "world"
/// ```
#[rhai_fn(name = "max")]
pub fn max_string(string1: ImmutableString, string2: ImmutableString) -> ImmutableString {
if string1 >= string2 {
string1
} else {
string2
}
}
/// Return the string that is lexically smaller than the other string.
///
/// # Example
///
/// ```rhai
/// min("hello", "world"); // returns "hello"
/// ```
#[rhai_fn(name = "min")]
pub fn min_string(string1: ImmutableString, string2: ImmutableString) -> ImmutableString {
if string1 <= string2 {
string1
} else {
string2
}
}
/// Return the character that is lexically greater than the other character.
///
/// # Example
///
/// ```rhai
/// max('h', 'w'); // returns 'w'
/// ```
#[rhai_fn(name = "max")]
pub fn max_char(char1: char, char2: char) -> char {
if char1 >= char2 {
char1
} else {
char2
}
}
/// Return the character that is lexically smaller than the other character.
///
/// # Example
///
/// ```rhai
/// max('h', 'w'); // returns 'h'
/// ```
#[rhai_fn(name = "min")]
pub fn min_char(char1: char, char2: char) -> char {
if char1 <= char2 {
char1
} else {
char2
}
}

#[cfg(not(feature = "no_index"))]
pub mod arrays {
Expand Down
8 changes: 8 additions & 0 deletions src/types/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub enum EvalAltResult {

/// Number of operations over maximum limit.
ErrorTooManyOperations(Position),
/// Number of variables over maximum limit.
ErrorTooManyVariables(Position),
/// [Modules][crate::Module] over maximum limit.
ErrorTooManyModules(Position),
/// Call stack over maximum limit.
Expand Down Expand Up @@ -166,6 +168,7 @@ impl fmt::Display for EvalAltResult {
Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?,
Self::ErrorFor(..) => f.write_str("For loop expects iterable type")?,
Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
Self::ErrorTooManyVariables(..) => f.write_str("Too many variables defined")?,
Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
Self::ErrorTerminated(..) => f.write_str("Script terminated")?,
Expand Down Expand Up @@ -331,6 +334,7 @@ impl EvalAltResult {
Self::ErrorCustomSyntax(..) => false,

Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyVariables(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..)
Expand All @@ -350,6 +354,7 @@ impl EvalAltResult {
| Self::ErrorParsing(..)
| Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyVariables(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..)
Expand Down Expand Up @@ -379,6 +384,7 @@ impl EvalAltResult {
| Self::ErrorFor(..)
| Self::ErrorArithmetic(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyVariables(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorRuntime(..) => (),
Expand Down Expand Up @@ -483,6 +489,7 @@ impl EvalAltResult {
| Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyVariables(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(.., pos)
Expand Down Expand Up @@ -543,6 +550,7 @@ impl EvalAltResult {
| Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyVariables(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(.., pos)
Expand Down
Loading

0 comments on commit ed5bbf0

Please sign in to comment.