Check data size after assignments.

This commit is contained in:
Stephen Chung 2021-06-11 19:59:50 +08:00
parent 07d25b4fb6
commit f9dcfeb1ad
3 changed files with 93 additions and 59 deletions

View File

@ -8,6 +8,7 @@ Bug fixes
--------- ---------
* Fixed incorrect optimization regarding chain-indexing with non-numeric index. * Fixed incorrect optimization regarding chain-indexing with non-numeric index.
* Variable values are checked for over-sized violations after assignments and setters.
Breaking changes Breaking changes
---------------- ----------------

View File

@ -1233,40 +1233,47 @@ impl Engine {
} }
// xxx[rhs] op= new_val // xxx[rhs] op= new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
let ((mut new_val, new_pos), (op_info, op_pos)) = let ((new_val, new_pos), (op_info, op_pos)) =
new_val.expect("never fails because `new_val` is `Some`"); new_val.expect("never fails because `new_val` is `Some`");
let idx_val = idx_val.as_index_value(); let idx_val = idx_val.as_index_value();
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
let mut idx_val_for_setter = idx_val.clone(); let mut idx_val_for_setter = idx_val.clone();
match self.get_indexed_mut( let try_setter = match self.get_indexed_mut(
mods, state, lib, target, idx_val, pos, true, false, level, mods, state, lib, target, idx_val, pos, true, false, level,
) { ) {
// Indexed value is a reference - update directly // Indexed value is a reference - update directly
Ok(obj_ptr) => { Ok(ref mut obj_ptr) => {
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
) )
.map_err(|err| err.fill_position(new_pos))?; .map_err(|err| err.fill_position(new_pos))?;
return Ok((Dynamic::UNIT, true)); None
} }
// Can't index - try to call an index setter // Can't index - try to call an index setter
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => {} Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => {
Some(new_val)
}
// Any other error // Any other error
Err(err) => return Err(err), Err(err) => return Err(err),
} };
if let Some(mut new_val) = try_setter {
// Try to call index setter // Try to call index setter
let hash_set = let hash_set =
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3)); FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true, new_pos, mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
None, level, new_pos, None, level,
)?; )?;
}
self.check_data_size(target.as_ref())
.map_err(|err| err.fill_position(root.1))?;
Ok((Dynamic::UNIT, true)) Ok((Dynamic::UNIT, true))
} }
@ -1304,15 +1311,19 @@ impl Engine {
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => { Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
let (name, pos) = &x.2; let (name, pos) = &x.2;
let index = name.into(); let index = name.into();
let val = self.get_indexed_mut( {
let mut val = self.get_indexed_mut(
mods, state, lib, target, index, *pos, true, false, level, mods, state, lib, target, index, *pos, true, false, level,
)?; )?;
let ((new_val, new_pos), (op_info, op_pos)) = let ((new_val, new_pos), (op_info, op_pos)) =
new_val.expect("never fails because `new_val` is `Some`"); new_val.expect("never fails because `new_val` is `Some`");
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, val, root, new_val, mods, state, lib, op_info, op_pos, &mut val, root, new_val,
) )
.map_err(|err| err.fill_position(new_pos))?; .map_err(|err| err.fill_position(new_pos))?;
}
self.check_data_size(target.as_ref())
.map_err(|err| err.fill_position(root.1))?;
Ok((Dynamic::UNIT, true)) Ok((Dynamic::UNIT, true))
} }
// {xxx:map}.id // {xxx:map}.id
@ -1322,7 +1333,6 @@ impl Engine {
let val = self.get_indexed_mut( let val = self.get_indexed_mut(
mods, state, lib, target, index, *pos, false, false, level, mods, state, lib, target, index, *pos, false, false, level,
)?; )?;
Ok((val.take_or_clone(), false)) Ok((val.take_or_clone(), false))
} }
// xxx.id op= ??? // xxx.id op= ???
@ -1357,11 +1367,22 @@ impl Engine {
} }
_ => Err(err), _ => Err(err),
})?; })?;
let obj_ptr = (&mut orig_val).into();
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, mods,
state,
lib,
op_info,
op_pos,
&mut (&mut orig_val).into(),
root,
new_val,
) )
.map_err(|err| err.fill_position(new_pos))?; .map_err(|err| err.fill_position(new_pos))?;
self.check_data_size(target.as_ref())
.map_err(|err| err.fill_position(root.1))?;
new_val = orig_val; new_val = orig_val;
} }
@ -1544,6 +1565,8 @@ impl Engine {
_ => Err(err), _ => Err(err),
}, },
)?; )?;
self.check_data_size(target.as_ref())
.map_err(|err| err.fill_position(root.1))?;
} }
Ok((result, may_be_changed)) Ok((result, may_be_changed))
@ -1989,18 +2012,23 @@ impl Engine {
for expr in x.iter() { for expr in x.iter() {
let item = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let item = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
self.eval_op_assignment( self.eval_op_assignment(
mods, mods,
state, state,
lib, lib,
Some(OpAssignment::new(TOKEN_OP_CONCAT)), Some(OpAssignment::new(TOKEN_OP_CONCAT)),
pos, pos,
(&mut result).into(), &mut (&mut result).into(),
("", Position::NONE), ("", Position::NONE),
item, item,
) )
.map_err(|err| err.fill_position(expr.position()))?; .map_err(|err| err.fill_position(expr.position()))?;
pos = expr.position(); pos = expr.position();
self.check_data_size(&result)
.map_err(|err| err.fill_position(pos))?;
} }
assert!( assert!(
@ -2125,11 +2153,8 @@ impl Engine {
_ => unreachable!("expression cannot be evaluated: {:?}", expr), _ => unreachable!("expression cannot be evaluated: {:?}", expr),
}; };
#[cfg(not(feature = "unchecked"))] self.check_return_value(result)
self.check_data_size(&result) .map_err(|err| err.fill_position(expr.position()))
.map_err(|err| err.fill_position(expr.position()))?;
result
} }
/// Evaluate a statements block. /// Evaluate a statements block.
@ -2217,7 +2242,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
op_info: Option<OpAssignment>, op_info: Option<OpAssignment>,
op_pos: Position, op_pos: Position,
mut target: Target, target: &mut Target,
root: (&str, Position), root: (&str, Position),
mut new_val: Dynamic, mut new_val: Dynamic,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
@ -2311,7 +2336,7 @@ impl Engine {
let rhs_val = self let rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
.flatten(); .flatten();
let (lhs_ptr, pos) = let (mut lhs_ptr, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?; self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
let var_name = lhs_expr let var_name = lhs_expr
@ -2332,12 +2357,15 @@ impl Engine {
lib, lib,
op_info.clone(), op_info.clone(),
*op_pos, *op_pos,
lhs_ptr, &mut lhs_ptr,
(var_name, pos), (var_name, pos),
rhs_val, rhs_val,
) )
.map_err(|err| err.fill_position(rhs_expr.position()))?; .map_err(|err| err.fill_position(rhs_expr.position()))?;
self.check_data_size(lhs_ptr.as_ref())
.map_err(|err| err.fill_position(lhs_expr.position()))?;
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
@ -2914,36 +2942,25 @@ impl Engine {
} }
}; };
#[cfg(not(feature = "unchecked"))] self.check_return_value(result)
self.check_data_size(&result) .map_err(|err| err.fill_position(stmt.position()))
.map_err(|err| err.fill_position(stmt.position()))?; }
/// Check a result to ensure that the data size is within allowable limit.
#[cfg(feature = "unchecked")]
#[inline(always)]
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
result result
} }
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> { #[inline(always)]
let result = match result { fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
Err(_) => return Ok(()), result.and_then(|r| self.check_data_size(&r).map(|_| r))
Ok(r) => r,
};
// If no data size limits, just return
let mut _has_limit = self.limits.max_string_size.is_some();
#[cfg(not(feature = "no_index"))]
{
_has_limit = _has_limit || self.limits.max_array_size.is_some();
}
#[cfg(not(feature = "no_object"))]
{
_has_limit = _has_limit || self.limits.max_map_size.is_some();
}
if !_has_limit {
return Ok(());
} }
fn check_data_size(&self, value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
// Recursively calculate the size of a value (especially `Array` and `Map`) // Recursively calculate the size of a value (especially `Array` and `Map`)
fn calc_size(value: &Dynamic) -> (usize, usize, usize) { fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
match value { match value {
@ -2996,7 +3013,22 @@ impl Engine {
} }
} }
let (_arr, _map, s) = calc_size(result); // If no data size limits, just return
let mut _has_limit = self.limits.max_string_size.is_some();
#[cfg(not(feature = "no_index"))]
{
_has_limit = _has_limit || self.limits.max_array_size.is_some();
}
#[cfg(not(feature = "no_object"))]
{
_has_limit = _has_limit || self.limits.max_map_size.is_some();
}
if !_has_limit {
return Ok(());
}
let (_arr, _map, s) = calc_size(value);
if s > self if s > self
.limits .limits

View File

@ -331,6 +331,7 @@ impl Engine {
.as_ref() .as_ref()
.or_else(|| state_source.as_ref()) .or_else(|| state_source.as_ref())
.map(|s| s.as_str()); .map(|s| s.as_str());
let result = if func.is_plugin_fn() { let result = if func.is_plugin_fn() {
func.get_plugin_fn() func.get_plugin_fn()
.call((self, fn_name, source, mods, lib).into(), args) .call((self, fn_name, source, mods, lib).into(), args)