Yield error on deep AST recursion

This commit is contained in:
Dirkjan Ochtman 2024-01-17 22:20:32 +01:00
parent 2c7759c0d0
commit 250828a6d2
3 changed files with 16 additions and 3 deletions

View File

@ -214,14 +214,20 @@ impl<'a> Expr<'a> {
Ok((i, res)) Ok((i, res))
} }
fn prefix(i: &'a str, level: Level) -> ParseResult<'a, Self> { fn prefix(i: &'a str, mut level: Level) -> ParseResult<'a, Self> {
let (_, level) = level.nest(i)?; let (_, nested) = level.nest(i)?;
let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), |i| { let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), |i| {
Suffix::parse(i, level) Suffix::parse(i, nested)
})(i)?; })(i)?;
for op in ops.iter().rev() { for op in ops.iter().rev() {
// This is a rare place where we create recursion in the parsed AST
// without recursing the parser call stack. However, this can lead
// to stack overflows in drop glue when the AST is very deep.
level = level.nest(i)?.1;
expr = Self::Unary(op, Box::new(expr)); expr = Self::Unary(op, Box::new(expr));
} }
Ok((i, expr)) Ok((i, expr))
} }

View File

@ -807,3 +807,9 @@ fn fuzzed_target_recursion() {
const TEMPLATE: &str = include_str!("../tests/target-recursion.txt"); const TEMPLATE: &str = include_str!("../tests/target-recursion.txt");
assert!(Ast::from_str(TEMPLATE, &Syntax::default()).is_err()); assert!(Ast::from_str(TEMPLATE, &Syntax::default()).is_err());
} }
#[test]
fn fuzzed_unary_recursion() {
const TEMPLATE: &str = include_str!("../tests/unary-recursion.txt");
assert!(Ast::from_str(TEMPLATE, &Syntax::default()).is_err());
}

File diff suppressed because one or more lines are too long