Prevent unbounded recursion when parsing expressions (#1248)
This commit is contained in:
parent
0e4b443742
commit
bfceb8f9c9
@ -198,6 +198,9 @@ impl Display for CompileError<'_> {
|
|||||||
parameter
|
parameter
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
ParsingRecursionDepthExceeded => {
|
||||||
|
write!(f, "Parsing recursion depth exceeded")?;
|
||||||
|
}
|
||||||
RequiredParameterFollowsDefaultParameter { parameter } => {
|
RequiredParameterFollowsDefaultParameter { parameter } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -74,6 +74,7 @@ pub(crate) enum CompileErrorKind<'src> {
|
|||||||
ParameterShadowsVariable {
|
ParameterShadowsVariable {
|
||||||
parameter: &'src str,
|
parameter: &'src str,
|
||||||
},
|
},
|
||||||
|
ParsingRecursionDepthExceeded,
|
||||||
RequiredParameterFollowsDefaultParameter {
|
RequiredParameterFollowsDefaultParameter {
|
||||||
parameter: &'src str,
|
parameter: &'src str,
|
||||||
},
|
},
|
||||||
|
@ -32,6 +32,8 @@ pub(crate) struct Parser<'tokens, 'src> {
|
|||||||
next: usize,
|
next: usize,
|
||||||
/// Current expected tokens
|
/// Current expected tokens
|
||||||
expected: BTreeSet<TokenKind>,
|
expected: BTreeSet<TokenKind>,
|
||||||
|
/// Current recursion depth
|
||||||
|
depth: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tokens, 'src> Parser<'tokens, 'src> {
|
impl<'tokens, 'src> Parser<'tokens, 'src> {
|
||||||
@ -46,6 +48,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
next: 0,
|
next: 0,
|
||||||
expected: BTreeSet::new(),
|
expected: BTreeSet::new(),
|
||||||
tokens,
|
tokens,
|
||||||
|
depth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,19 +394,31 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
|
|
||||||
/// Parse an expression, e.g. `1 + 2`
|
/// Parse an expression, e.g. `1 + 2`
|
||||||
fn parse_expression(&mut self) -> CompileResult<'src, Expression<'src>> {
|
fn parse_expression(&mut self) -> CompileResult<'src, Expression<'src>> {
|
||||||
if self.accepted_keyword(Keyword::If)? {
|
if self.depth == if cfg!(windows) { 64 } else { 255 } {
|
||||||
self.parse_conditional()
|
return Err(CompileError {
|
||||||
|
token: self.next()?,
|
||||||
|
kind: CompileErrorKind::ParsingRecursionDepthExceeded,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.depth += 1;
|
||||||
|
|
||||||
|
let expression = if self.accepted_keyword(Keyword::If)? {
|
||||||
|
self.parse_conditional()?
|
||||||
} else {
|
} else {
|
||||||
let value = self.parse_value()?;
|
let value = self.parse_value()?;
|
||||||
|
|
||||||
if self.accepted(Plus)? {
|
if self.accepted(Plus)? {
|
||||||
let lhs = Box::new(value);
|
let lhs = Box::new(value);
|
||||||
let rhs = Box::new(self.parse_expression()?);
|
let rhs = Box::new(self.parse_expression()?);
|
||||||
Ok(Expression::Concatenation { lhs, rhs })
|
Expression::Concatenation { lhs, rhs }
|
||||||
} else {
|
} else {
|
||||||
Ok(value)
|
value
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.depth -= 1;
|
||||||
|
Ok(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a conditional, e.g. `if a == b { "foo" } else { "bar" }`
|
/// Parse a conditional, e.g. `if a == b { "foo" } else { "bar" }`
|
||||||
|
@ -62,6 +62,7 @@ mod positional_arguments;
|
|||||||
mod quiet;
|
mod quiet;
|
||||||
mod quote;
|
mod quote;
|
||||||
mod readme;
|
mod readme;
|
||||||
|
mod recursion_limit;
|
||||||
mod regexes;
|
mod regexes;
|
||||||
mod run;
|
mod run;
|
||||||
mod search;
|
mod search;
|
||||||
|
30
tests/recursion_limit.rs
Normal file
30
tests/recursion_limit.rs
Normal 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 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
|
||||||
|
| ^
|
||||||
|
";
|
Loading…
Reference in New Issue
Block a user