From 57f7bb8e6e5bf469a0cfa12f2145e53de137ac03 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 31 Jul 2024 12:02:16 -0500 Subject: [PATCH] fix(parser): Resolve stackoverflow on lots of blank lines --- Cargo.lock | 4 ++-- crates/toml_edit/Cargo.toml | 2 +- crates/toml_edit/src/parser/trivia.rs | 26 +++++++++++++++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e630250..ef4ec4d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1175,9 +1175,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.16" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" +checksum = "93b68c91a1b24c7456960ac3290e86a316f3aefcda89c4cad24ae3eda34f4411" dependencies = [ "memchr", ] diff --git a/crates/toml_edit/Cargo.toml b/crates/toml_edit/Cargo.toml index 355d7f72..aea168f8 100644 --- a/crates/toml_edit/Cargo.toml +++ b/crates/toml_edit/Cargo.toml @@ -41,7 +41,7 @@ unbounded = [] [dependencies] indexmap = { version = "2.0.0", features = ["std"] } -winnow = { version = "0.6.16", optional = true } +winnow = { version = "0.6.17", optional = true } serde = { version = "1.0.145", optional = true } kstring = { version = "2.0.0", features = ["max_inline"], optional = true } toml_datetime = { version = "0.6.8", path = "../toml_datetime" } diff --git a/crates/toml_edit/src/parser/trivia.rs b/crates/toml_edit/src/parser/trivia.rs index e8ab25ef..acf678b9 100644 --- a/crates/toml_edit/src/parser/trivia.rs +++ b/crates/toml_edit/src/parser/trivia.rs @@ -9,6 +9,7 @@ use winnow::combinator::peek; use winnow::combinator::repeat; use winnow::combinator::terminated; use winnow::prelude::*; +use winnow::stream::Stream as _; use winnow::token::any; use winnow::token::one_of; use winnow::token::take_while; @@ -91,15 +92,26 @@ pub(crate) fn ws_newlines(input: &mut Input<'_>) -> PResult<()> { // note: this rule is not present in the original grammar // ws-comment-newline = *( ws-newline-nonempty / comment ) pub(crate) fn ws_comment_newline(input: &mut Input<'_>) -> PResult<()> { - let _ = ws.parse_next(input)?; + let mut start = input.checkpoint(); + loop { + let _ = ws.parse_next(input)?; + + dispatch! {opt(peek(any)); + Some(b'#') => (comment, newline).void(), + Some(b'\n') => (newline).void(), + Some(b'\r') => (newline).void(), + _ => empty, + } + .parse_next(input)?; - dispatch! {opt(peek(any)); - Some(b'#') => (comment, newline, ws_comment_newline).void(), - Some(b'\n') => (newline, ws_comment_newline).void(), - Some(b'\r') => (newline, ws_comment_newline).void(), - _ => empty, + let end = input.checkpoint(); + if start == end { + break; + } + start = end; } - .parse_next(input) + + Ok(()) } // note: this rule is not present in the original grammar