Skip to content

Commit

Permalink
Merge pull request #898 from schungx/master
Browse files Browse the repository at this point in the history
Fix bits iterator range bug.
  • Loading branch information
schungx authored Jul 13, 2024
2 parents 3ea3c82 + 60e76b5 commit 03730d8
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 27 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ Rhai Release Notes
Version 1.20.0
==============

Bug fixes
---------

* (Fuzzing) An integer-overflow bug from an inclusive range in the bits iterator is fixed.

Enhancements
------------

* The `break`, `return` and `throw` statements can now be simply used as `switch` case statement expressions. Previously it is required that the statement be wrapped in a block.


Version 1.19.0
==============

Expand Down
9 changes: 8 additions & 1 deletion src/packages/iter_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,14 @@ mod iterator_functions {
pub fn bits_from_inclusive_range(value: INT, range: InclusiveRange) -> RhaiResultOf<BitRange> {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
BitRange::new(value, from, to - from + 1)

// It is OK to use `INT::MAX` as the length to avoid an addition overflow
// even though it is an off-by-one error because there cannot be so many bits anyway.
#[cfg(not(feature = "unchecked"))]
return BitRange::new(value, from, (to - from).checked_add(1).unwrap_or(INT::MAX));

#[cfg(feature = "unchecked")]
return BitRange::new(value, from, to - from + 1);
}
/// Return an iterator over a portion of bits in the number.
///
Expand Down
47 changes: 21 additions & 26 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3328,18 +3328,18 @@ impl Engine {
{
let pos = eat_token(state.input, &Token::Break);

let expr = match state.input.peek().unwrap() {
// `break` at <EOF>
(Token::EOF, ..) => None,
// `break` at end of block
(Token::RightBrace, ..) => None,
// `break;`
(Token::SemiColon, ..) => None,
// `break` with expression
_ => Some(self.parse_expr(state, settings.level_up()?)?.into()),
};
let current_pos = state.input.peek().unwrap().1;

Ok(Stmt::BreakLoop(expr, ASTFlags::BREAK, pos))
match self.parse_expr(state, settings.level_up()?) {
Ok(expr) => Ok(Stmt::BreakLoop(Some(expr.into()), ASTFlags::BREAK, pos)),
Err(err) => {
if state.input.peek().unwrap().1 == current_pos {
Ok(Stmt::BreakLoop(None, ASTFlags::BREAK, pos))
} else {
return Err(err);
}
}
}
}
Token::Continue | Token::Break if self.allow_looping() => {
Err(PERR::LoopBreak.into_err(token_pos))
Expand All @@ -3362,21 +3362,16 @@ impl Engine {
})
.unwrap();

match state.input.peek().unwrap() {
// `return`/`throw` at <EOF>
(Token::EOF, ..) => Ok(Stmt::Return(None, return_type, token_pos)),
// `return`/`throw` at end of block
(Token::RightBrace, ..)
if !settings.has_flag(ParseSettingFlags::GLOBAL_LEVEL) =>
{
Ok(Stmt::Return(None, return_type, token_pos))
}
// `return;` or `throw;`
(Token::SemiColon, ..) => Ok(Stmt::Return(None, return_type, token_pos)),
// `return` or `throw` with expression
_ => {
let expr = self.parse_expr(state, settings.level_up()?)?;
Ok(Stmt::Return(Some(expr.into()), return_type, token_pos))
let current_pos = state.input.peek().unwrap().1;

match self.parse_expr(state, settings.level_up()?) {
Ok(expr) => Ok(Stmt::Return(Some(expr.into()), return_type, token_pos)),
Err(err) => {
if state.input.peek().unwrap().1 == current_pos {
Ok(Stmt::Return(None, return_type, token_pos))
} else {
return Err(err);
}
}
}
}
Expand Down

0 comments on commit 03730d8

Please sign in to comment.