Skip to content

Commit

Permalink
Merge pull request #788 from schungx/master
Browse files Browse the repository at this point in the history
Fix negative indexing bug.
  • Loading branch information
schungx committed Nov 29, 2023
2 parents 269302c + ebd9f72 commit 1ea32dc
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 128 deletions.
32 changes: 17 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,33 @@ Rhai Release Notes
Version 1.17.0
==============

In this version, fuzzing via [Google OSS-Fuzz](https://github.com/google/oss-fuzz) is used to flush
out hidden bugs and edge cases. This should result in higher code quality, better stability and
improved security.

Potentially breaking changes
----------------------------

* `ImmutableString` now derefs to `&str` instead of `&SmartString`. Normally this should not be a breaking change.
* Traits implemented by `ImmutableString` are cleaned up. However, I cannot guarantee that there are absolutely no breaking changes, although I try to be careful.
* `EvalContext::new`, `FloatWrapper` and `ConditionalExpr` are now exported only under `internals`.
* `AST::clear_doc` is removed.

Bug fixes
----------

* Fixed crash when parsing multi-segment interpolated string longer than maximum (found via fuzzing).
* Fixed crash when parsing unterminated comment (found via fuzzing).
* Fixed crash when parsing deeply-nested right-associated operators such as `**` (found via fuzzing).
* Fixed crash when parsing combo-chaining expressions such as `(a.b).c` (found via fuzzing).
* Fixed crash when calling functions that have `Dynamic` parameters with more than 16 parameters (found via fuzzing).
* Traits implemented by `ImmutableString` are cleaned up. Normally this should not be a breaking change.
* `EvalContext::new`, `FloatWrapper` and `ConditionalExpr` are now gated under `internals`.

Deprecated API's
----------------

* `rhai::config::hashing::set_ahash_seed`, `rhai::config::hashing::get_ahash_seed` and the `RHAI_AHASH_SEED` environment variable are deprecated in favor of `rhai::config::hashing::set_hashing_seed`, `rhai::config::hashing::get_hashing_seed` and `RHAI_HASHING_SEED`.
* `AST::clear_doc` is deprecated.

New features
------------
Fixes to bugs found via fuzzing
-------------------------------

* Fuzzing is now done via [Google OSS-Fuzz](https://github.com/google/oss-fuzz).
* Fixed crash when parsing multi-segment interpolated string longer than maximum.
* Fixed crash when parsing unterminated comment.
* Fixed crash when parsing deeply-nested right-associated operators such as `**`.
* Fixed crash when parsing combo-chaining expressions such as `(a.b).c`.
* Fixed crash when calling functions that have `Dynamic` parameters with more than 16 parameters.
* Fixed crash when indexing into an empty array with negative index.
* Indexing into an array with a negative index that is larger than the length of the array now returns an out-of-bounds error (similar to positive indices) instead of defaulting to the first element.

Enhancements
------------
Expand All @@ -40,6 +41,7 @@ Enhancements
* `Dynamic::is_fnptr` is made a public API.
* `Scope::get_value_ref` and `Scope::get_value_mut` are added.


Version 1.16.3
==============

Expand Down
13 changes: 13 additions & 0 deletions src/api/deprecated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,19 @@ impl AST {
pub fn lib(&self) -> &crate::Module {
self.shared_lib()
}
/// Clear the documentation.
/// Exported under the `metadata` feature only.
///
/// # Deprecated
///
/// This method will be removed in the next major version.
#[deprecated(since = "1.17.0")]
#[cfg(feature = "metadata")]
#[inline(always)]
pub fn clear_doc(&mut self) -> &mut Self {
self.doc.clear();
self
}
}

impl NativeCallContext<'_> {
Expand Down
78 changes: 39 additions & 39 deletions src/api/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,39 +48,39 @@ pub struct Limits {
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub max_call_stack_depth: usize,
pub call_stack_depth: usize,
/// Maximum depth of statements/expressions at global level.
pub max_expr_depth: Option<NonZeroUsize>,
pub 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: Option<NonZeroUsize>,
pub function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<NonZeroU64>,
pub operations: Option<NonZeroU64>,
/// Maximum number of variables allowed at any instant.
///
/// Set to zero to effectively disable creating variables.
pub max_variables: usize,
pub variables: usize,
/// Maximum number of [modules][crate::Module] allowed to load.
///
/// Set to zero to effectively disable loading any [module][crate::Module].
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
pub max_modules: usize,
pub modules: usize,
/// Maximum length of a [string][crate::ImmutableString].
pub max_string_len: Option<NonZeroUsize>,
pub string_len: Option<NonZeroUsize>,
/// Maximum length of an [array][crate::Array].
///
/// Not available under `no_index`.
#[cfg(not(feature = "no_index"))]
pub max_array_size: Option<NonZeroUsize>,
pub array_size: Option<NonZeroUsize>,
/// Maximum number of properties in an [object map][crate::Map].
///
/// Not available under `no_object`.
#[cfg(not(feature = "no_object"))]
pub max_map_size: Option<NonZeroUsize>,
pub map_size: Option<NonZeroUsize>,
}

impl Limits {
Expand All @@ -91,19 +91,19 @@ impl Limits {
pub const fn new() -> Self {
Self {
#[cfg(not(feature = "no_function"))]
max_call_stack_depth: default_limits::MAX_CALL_STACK_DEPTH,
max_expr_depth: NonZeroUsize::new(default_limits::MAX_EXPR_DEPTH),
call_stack_depth: default_limits::MAX_CALL_STACK_DEPTH,
expr_depth: NonZeroUsize::new(default_limits::MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))]
max_function_expr_depth: NonZeroUsize::new(default_limits::MAX_FUNCTION_EXPR_DEPTH),
max_operations: None,
max_variables: usize::MAX,
function_expr_depth: NonZeroUsize::new(default_limits::MAX_FUNCTION_EXPR_DEPTH),
operations: None,
variables: usize::MAX,
#[cfg(not(feature = "no_module"))]
max_modules: usize::MAX,
max_string_len: None,
modules: usize::MAX,
string_len: None,
#[cfg(not(feature = "no_index"))]
max_array_size: None,
array_size: None,
#[cfg(not(feature = "no_object"))]
max_map_size: None,
map_size: None,
}
}
}
Expand All @@ -120,19 +120,19 @@ impl Engine {
/// Is there a data size limit set?
#[inline(always)]
pub(crate) const fn has_data_size_limit(&self) -> bool {
self.limits.max_string_len.is_some()
self.limits.string_len.is_some()
|| {
#[cfg(not(feature = "no_index"))]
{
self.limits.max_array_size.is_some()
self.limits.array_size.is_some()
}
#[cfg(feature = "no_index")]
false
}
|| {
#[cfg(not(feature = "no_object"))]
{
self.limits.max_map_size.is_some()
self.limits.map_size.is_some()
}
#[cfg(feature = "no_object")]
false
Expand All @@ -145,7 +145,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn set_max_call_levels(&mut self, levels: usize) -> &mut Self {
self.limits.max_call_stack_depth = levels;
self.limits.call_stack_depth = levels;
self
}
/// The maximum levels of function calls allowed for a script.
Expand All @@ -155,7 +155,7 @@ impl Engine {
#[must_use]
pub const fn max_call_levels(&self) -> usize {
#[cfg(not(feature = "no_function"))]
return self.limits.max_call_stack_depth;
return self.limits.call_stack_depth;
#[cfg(feature = "no_function")]
return 0;
}
Expand All @@ -165,7 +165,7 @@ impl Engine {
/// Not available under `unchecked`.
#[inline(always)]
pub fn set_max_operations(&mut self, operations: u64) -> &mut Self {
self.limits.max_operations = NonZeroU64::new(operations);
self.limits.operations = NonZeroU64::new(operations);
self
}
/// The maximum number of operations allowed for a script to run (0 for unlimited).
Expand All @@ -174,7 +174,7 @@ impl Engine {
#[inline]
#[must_use]
pub const fn max_operations(&self) -> u64 {
match self.limits.max_operations {
match self.limits.operations {
Some(n) => n.get(),
None => 0,
}
Expand All @@ -184,7 +184,7 @@ impl Engine {
/// Not available under `unchecked`.
#[inline(always)]
pub fn set_max_variables(&mut self, modules: usize) -> &mut Self {
self.limits.max_variables = modules;
self.limits.variables = modules;
self
}
/// The maximum number of imported variables allowed for a script at any instant.
Expand All @@ -193,15 +193,15 @@ impl Engine {
#[inline(always)]
#[must_use]
pub const fn max_variables(&self) -> usize {
self.limits.max_variables
self.limits.variables
}
/// Set the maximum number of imported [modules][crate::Module] allowed for a script.
///
/// Not available under `unchecked` or `no_module`.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn set_max_modules(&mut self, modules: usize) -> &mut Self {
self.limits.max_modules = modules;
self.limits.modules = modules;
self
}
/// The maximum number of imported [modules][crate::Module] allowed for a script.
Expand All @@ -211,7 +211,7 @@ impl Engine {
#[must_use]
pub const fn max_modules(&self) -> usize {
#[cfg(not(feature = "no_module"))]
return self.limits.max_modules;
return self.limits.modules;
#[cfg(feature = "no_module")]
return 0;
}
Expand All @@ -224,10 +224,10 @@ impl Engine {
max_expr_depth: usize,
#[cfg(not(feature = "no_function"))] max_function_expr_depth: usize,
) -> &mut Self {
self.limits.max_expr_depth = NonZeroUsize::new(max_expr_depth);
self.limits.expr_depth = NonZeroUsize::new(max_expr_depth);
#[cfg(not(feature = "no_function"))]
{
self.limits.max_function_expr_depth = NonZeroUsize::new(max_function_expr_depth);
self.limits.function_expr_depth = NonZeroUsize::new(max_function_expr_depth);
}
self
}
Expand All @@ -237,7 +237,7 @@ impl Engine {
#[inline]
#[must_use]
pub const fn max_expr_depth(&self) -> usize {
match self.limits.max_expr_depth {
match self.limits.expr_depth {
Some(n) => n.get(),
None => 0,
}
Expand All @@ -249,7 +249,7 @@ impl Engine {
#[must_use]
pub const fn max_function_expr_depth(&self) -> usize {
#[cfg(not(feature = "no_function"))]
return match self.limits.max_function_expr_depth {
return match self.limits.function_expr_depth {
Some(n) => n.get(),
None => 0,
};
Expand All @@ -261,7 +261,7 @@ impl Engine {
/// Not available under `unchecked`.
#[inline(always)]
pub fn set_max_string_size(&mut self, max_len: usize) -> &mut Self {
self.limits.max_string_len = NonZeroUsize::new(max_len);
self.limits.string_len = NonZeroUsize::new(max_len);
self
}
/// The maximum length, in bytes, of [strings][crate::ImmutableString] (0 for unlimited).
Expand All @@ -270,7 +270,7 @@ impl Engine {
#[inline]
#[must_use]
pub const fn max_string_size(&self) -> usize {
match self.limits.max_string_len {
match self.limits.string_len {
Some(n) => n.get(),
None => 0,
}
Expand All @@ -281,7 +281,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 = NonZeroUsize::new(max_size);
self.limits.array_size = NonZeroUsize::new(max_size);
self
}
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
Expand All @@ -291,7 +291,7 @@ impl Engine {
#[must_use]
pub const fn max_array_size(&self) -> usize {
#[cfg(not(feature = "no_index"))]
return match self.limits.max_array_size {
return match self.limits.array_size {
Some(n) => n.get(),
None => 0,
};
Expand All @@ -304,7 +304,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 = NonZeroUsize::new(max_size);
self.limits.map_size = NonZeroUsize::new(max_size);
self
}
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
Expand All @@ -314,7 +314,7 @@ impl Engine {
#[must_use]
pub const fn max_map_size(&self) -> usize {
#[cfg(not(feature = "no_object"))]
return match self.limits.max_map_size {
return match self.limits.map_size {
Some(n) => n.get(),
None => 0,
};
Expand Down
6 changes: 5 additions & 1 deletion src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ impl fmt::Debug for Engine {
#[cfg(not(feature = "unchecked"))]
f.field("progress", &self.progress.is_some());

f.field("options", &self.options);
f.field("options", &self.options)
.field("default_tag", &self.def_tag);

#[cfg(not(feature = "no_optimize"))]
f.field("optimization_level", &self.optimization_level);

#[cfg(not(feature = "unchecked"))]
f.field("limits", &self.limits);
Expand Down

0 comments on commit 1ea32dc

Please sign in to comment.