From 60aa2dc03a22d69c8800fec81f99c96958a11363 Mon Sep 17 00:00:00 2001 From: Ludwig Stecher Date: Sun, 19 Jun 2022 00:16:30 +0200 Subject: [PATCH] Add recursion limit --- rulex-lib/src/error/parse_error.rs | 3 +++ rulex-lib/src/lib.rs | 2 +- rulex-lib/src/parse/input.rs | 27 +++++++++++++++++++----- rulex-lib/src/parse/parsers.rs | 33 +++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/rulex-lib/src/error/parse_error.rs b/rulex-lib/src/error/parse_error.rs index ac4ad8a..c41562c 100644 --- a/rulex-lib/src/error/parse_error.rs +++ b/rulex-lib/src/error/parse_error.rs @@ -95,6 +95,9 @@ pub(crate) enum ParseErrorKind { #[error(transparent)] Unsupported(UnsupportedError), + #[error("Recursion limit reached. Try a less nested expression")] + RecursionLimit, + #[error("Unknown error: {:?}", .0)] Nom(nom::error::ErrorKind), #[error("Incomplete parse")] diff --git a/rulex-lib/src/lib.rs b/rulex-lib/src/lib.rs index ca56570..c944da8 100644 --- a/rulex-lib/src/lib.rs +++ b/rulex-lib/src/lib.rs @@ -84,7 +84,7 @@ impl<'i> Rulex<'i> { /// The parsed `Rulex` can be displayed with `Debug` if the `dbg` feature is /// enabled. pub fn parse(input: &'i str, options: ParseOptions) -> Result { - let rule = parse::parse(input)?; + let rule = parse::parse(input, 256)?; rule.validate(&options)?; Ok(Rulex(rule)) } diff --git a/rulex-lib/src/parse/input.rs b/rulex-lib/src/parse/input.rs index acf8a76..7842341 100644 --- a/rulex-lib/src/parse/input.rs +++ b/rulex-lib/src/parse/input.rs @@ -13,10 +13,15 @@ use super::token::Token; pub(crate) struct Input<'i, 'b> { source: &'i str, tokens: &'b [(Token, Span)], + recursion: u16, } impl<'i, 'b> Input<'i, 'b> { - pub(super) fn from(source: &'i str, tokens: &'b [(Token, Span)]) -> Result { + pub(super) fn from( + source: &'i str, + tokens: &'b [(Token, Span)], + recursion: u16, + ) -> Result { let error = tokens.iter().find_map(|&(t, span)| match t { Token::Error => Some((span, None)), Token::ErrorMsg(m) => Some((span, Some(m))), @@ -29,7 +34,19 @@ impl<'i, 'b> Input<'i, 'b> { }; } - Ok(Input { source, tokens }) + Ok(Input { source, tokens, recursion }) + } + + pub(super) fn recursion_start(&mut self) -> Result<(), ParseError> { + self.recursion = self + .recursion + .checked_sub(1) + .ok_or_else(|| ParseErrorKind::RecursionLimit.at(self.span()))?; + Ok(()) + } + + pub(super) fn recursion_end(&mut self) { + self.recursion += 1; } pub(super) fn is_empty(&self) -> bool { @@ -101,7 +118,7 @@ impl<'i, 'b> InputIter for Input<'i, 'b> { } fn iter_elements(&self) -> Self::IterElem { - Input { source: self.source, tokens: self.tokens } + Input { ..*self } } fn position

(&self, predicate: P) -> Option @@ -131,12 +148,12 @@ impl<'i, 'b> InputTake for Input<'i, 'b> { fn take(&self, count: usize) -> Self { let tokens = &self.tokens[..count]; - Input { source: self.source, tokens } + Input { tokens, ..*self } } fn take_split(&self, count: usize) -> (Self, Self) { let (left, right) = self.tokens.split_at(count); - (Input { source: self.source, tokens: left }, Input { source: self.source, tokens: right }) + (Input { tokens: left, ..*self }, Input { tokens: right, ..*self }) } } diff --git a/rulex-lib/src/parse/parsers.rs b/rulex-lib/src/parse/parsers.rs index 116012c..0a7b0af 100644 --- a/rulex-lib/src/parse/parsers.rs +++ b/rulex-lib/src/parse/parsers.rs @@ -35,9 +35,9 @@ use super::{Input, Token}; pub(super) type PResult<'i, 'b, T> = IResult, T, ParseError>; -pub(crate) fn parse(source: &str) -> Result, ParseError> { +pub(crate) fn parse(source: &str, recursion: u16) -> Result, ParseError> { let tokens = super::tokenize::tokenize(source); - let input = Input::from(source, &tokens)?; + let input = Input::from(source, &tokens, recursion)?; let (rest, rules) = parse_modified(input)?; if rest.is_empty() { @@ -47,6 +47,22 @@ pub(crate) fn parse(source: &str) -> Result, ParseError> { } } +fn recurse<'i, 'b, O>( + mut parser: impl Parser, O, ParseError>, +) -> impl FnMut(Input<'i, 'b>) -> PResult<'i, 'b, O> { + move |mut input| { + input.recursion_start().map_err(nom::Err::Failure)?; + + match parser.parse(input) { + Ok((mut input, output)) => { + input.recursion_end(); + Ok((input, output)) + } + Err(e) => Err(e), + } + } +} + pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> { enum ModifierKind { Enable, @@ -78,7 +94,7 @@ pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru "let", cut(Token::Identifier), cut(Token::Equals), - cut(parse_or), + cut(recurse(parse_or)), cut(Token::Semicolon), )), |((_, span_start), (name, name_span), _, rule, (_, span_end))| { @@ -86,7 +102,7 @@ pub(super) fn parse_modified<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru }, ), ))), - parse_or, + recurse(parse_or), ), |(stmts, mut rule): (Vec<(Stmt, Span)>, _)| { if stmts.len() > 1 { @@ -137,7 +153,7 @@ pub(super) fn parse_sequence<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Ru pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule<'i>> { alt(( try_map( - pair(Token::Not, opt(parse_fixes)), + pair(Token::Not, opt(recurse(parse_fixes))), |(_, rule)| { if let Some(mut rule) = rule { rule.negate()?; @@ -148,7 +164,7 @@ pub(super) fn parse_fixes<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule< }, nom::Err::Failure, ), - map(pair(parse_lookaround, parse_modified), |((kind, span), rule)| { + map(pair(parse_lookaround, recurse(parse_modified)), |((kind, span), rule)| { let span = span.join(rule.span()); Rule::Lookaround(Box::new(Lookaround::new(rule, kind, span))) }), @@ -282,7 +298,10 @@ pub(super) fn parse_group<'i, 'b>(input: Input<'i, 'b>) -> PResult<'i, 'b, Rule< } map( - pair(opt(parse_capture), tuple((Token::OpenParen, parse_modified, cut(Token::CloseParen)))), + pair( + opt(parse_capture), + tuple((Token::OpenParen, recurse(parse_modified), cut(Token::CloseParen))), + ), |(capture, (_, rule, (_, close_paren)))| match (capture, rule) { (None, rule) => rule, (Some((capture, c_span)), Rule::Group(mut g)) if !g.is_capturing() => {