Prevent unbounded recursion when parsing expressions (#1248)

This commit is contained in:
Evan Richter 2022-06-22 17:00:13 -05:00 committed by GitHub
parent 0e4b443742
commit bfceb8f9c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 5 deletions

View file

@ -198,6 +198,9 @@ impl Display for CompileError<'_> {
parameter
)?;
}
ParsingRecursionDepthExceeded => {
write!(f, "Parsing recursion depth exceeded")?;
}
RequiredParameterFollowsDefaultParameter { parameter } => {
write!(
f,

View file

@ -74,6 +74,7 @@ pub(crate) enum CompileErrorKind<'src> {
ParameterShadowsVariable {
parameter: &'src str,
},
ParsingRecursionDepthExceeded,
RequiredParameterFollowsDefaultParameter {
parameter: &'src str,
},

View file

@ -32,6 +32,8 @@ pub(crate) struct Parser<'tokens, 'src> {
next: usize,
/// Current expected tokens
expected: BTreeSet<TokenKind>,
/// Current recursion depth
depth: u8,
}
impl<'tokens, 'src> Parser<'tokens, 'src> {
@ -46,6 +48,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
next: 0,
expected: BTreeSet::new(),
tokens,
depth: 0,
}
}
@ -391,19 +394,31 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
/// Parse an expression, e.g. `1 + 2`
fn parse_expression(&mut self) -> CompileResult<'src, Expression<'src>> {
if self.accepted_keyword(Keyword::If)? {
self.parse_conditional()
if self.depth == if cfg!(windows) { 64 } else { 255 } {
return Err(CompileError {
token: self.next()?,
kind: CompileErrorKind::ParsingRecursionDepthExceeded,
});
}
self.depth += 1;
let expression = if self.accepted_keyword(Keyword::If)? {
self.parse_conditional()?
} else {
let value = self.parse_value()?;
if self.accepted(Plus)? {
let lhs = Box::new(value);
let rhs = Box::new(self.parse_expression()?);
Ok(Expression::Concatenation { lhs, rhs })
Expression::Concatenation { lhs, rhs }
} else {
Ok(value)
value
}
}
};
self.depth -= 1;
Ok(expression)
}
/// Parse a conditional, e.g. `if a == b { "foo" } else { "bar" }`

View file

@ -62,6 +62,7 @@ mod positional_arguments;
mod quiet;
mod quote;
mod readme;
mod recursion_limit;
mod regexes;
mod run;
mod search;

30
tests/recursion_limit.rs Normal file
View file

@ -0,0 +1,30 @@
use super::*;
#[test]
fn bugfix() {
let mut justfile = String::from("foo: (x ");
for _ in 0..500 {
justfile.push('(');
}
Test::new()
.justfile(&justfile)
.stderr(RECURSION_LIMIT_REACHED)
.status(EXIT_FAILURE)
.run();
}
#[cfg(not(windows))]
const RECURSION_LIMIT_REACHED: &str = "
error: Parsing recursion depth exceeded
|
1 | foo: (x ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
| ^
";
#[cfg(windows)]
const RECURSION_LIMIT_REACHED: &str = "
error: Parsing recursion depth exceeded
|
1 | foo: (x ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
| ^
";