From 53ee86bb4f7a27736fa4418280c00da576553736 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Tue, 14 Jun 2022 03:35:49 +0200 Subject: [PATCH 1/3] Do not parse past Eof Parsing past Eof can lead to infinite loops, as it violates the assumptions of the termination checker. See: https://github.com/rescript-lang/syntax/pull/540 This PR makes `Parser.next` assert false when called on Eof. This should not happen as one should check the token before calling `Parser.next`. The one exception is during lookahead, for which we provide a `nextUnsafe` function which does not make progress. --- src/res_core.ml | 2 +- src/res_parser.ml | 6 +++++- src/res_parser.mli | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index 7d891f5d..0d2429d5 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -259,7 +259,7 @@ let isEs6ArrowExpression ~inTernary p = * *) false | _ -> - Parser.next state; + Parser.nextUnsafe state; (* error recovery, peek at the next token, * (elements, providerId] => { * in the example above, we have an unbalanced ] here diff --git a/src/res_parser.ml b/src/res_parser.ml index 6aa63f97..fb5e1b76 100644 --- a/src/res_parser.ml +++ b/src/res_parser.ml @@ -49,6 +49,7 @@ let endRegion p = * in the parser's state. Every comment contains the end position of its * previous token to facilite comment interleaving *) let rec next ?prevEndPos p = + if p.token = Eof then assert false; let prevEndPos = match prevEndPos with Some pos -> pos | None -> p.endPos in let (startPos, endPos, token) = Scanner.scan p.scanner in match token with @@ -65,6 +66,9 @@ let rec next ?prevEndPos p = p.startPos <- startPos; p.endPos <- endPos +let nextUnsafe p = + if p.token <> Eof then next p + let nextTemplateLiteralToken p = let (startPos, endPos, token) = Scanner.scanTemplateLiteralToken p.scanner in p.token <- token; @@ -82,7 +86,7 @@ let make ?(mode=ParseForTypeChecker) src filename = let parserState = { mode; scanner; - token = Token.Eof; + token = Token.Semicolon; startPos = Lexing.dummy_pos; prevEndPos = Lexing.dummy_pos; endPos = Lexing.dummy_pos; diff --git a/src/res_parser.mli b/src/res_parser.mli index 80a1c639..5f215ea6 100644 --- a/src/res_parser.mli +++ b/src/res_parser.mli @@ -28,6 +28,7 @@ val make: ?mode:mode -> string -> string -> t val expect: ?grammar:Grammar.t -> Token.t -> t -> unit val optional: t -> Token.t -> bool val next: ?prevEndPos:Lexing.position -> t -> unit +val nextUnsafe: t -> unit (* Does not assert on Eof, makes no progress *) val nextTemplateLiteralToken: t -> unit val lookahead: t -> (t -> 'a) -> 'a val err: From ce970c5e3f420c64f5de521eadf3609a2ad7d644 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 16 Jun 2022 01:43:23 +0200 Subject: [PATCH 2/3] Infinite loop on template gets caught. --- tests/parsing/infiniteLoops/expected/templateEof.res.txt | 1 + tests/parsing/infiniteLoops/templateEof.res | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/parsing/infiniteLoops/expected/templateEof.res.txt create mode 100644 tests/parsing/infiniteLoops/templateEof.res diff --git a/tests/parsing/infiniteLoops/expected/templateEof.res.txt b/tests/parsing/infiniteLoops/expected/templateEof.res.txt new file mode 100644 index 00000000..b3ca0a9a --- /dev/null +++ b/tests/parsing/infiniteLoops/expected/templateEof.res.txt @@ -0,0 +1 @@ +Fatal error: exception File "src/res_parser.ml", line 52, characters 23-29: Assertion failed diff --git a/tests/parsing/infiniteLoops/templateEof.res b/tests/parsing/infiniteLoops/templateEof.res new file mode 100644 index 00000000..1f8ee7c4 --- /dev/null +++ b/tests/parsing/infiniteLoops/templateEof.res @@ -0,0 +1,3 @@ +et foo = x => + switch x { + | `${ \ No newline at end of file From 753fda3afc538688d029fa24ced79e8fa57f86a7 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 16 Jun 2022 01:46:35 +0200 Subject: [PATCH 3/3] Defensive programming on potential infinite loop with Eof on lident recovery. --- src/res_core.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/res_core.ml b/src/res_core.ml index 0d2429d5..1dcd29cd 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -623,7 +623,7 @@ let rec parseLident p = None ) else ( let rec loop p = - if not (Recover.shouldAbortListParse p) + if not (Recover.shouldAbortListParse p) && p.token <> Eof then begin Parser.next p; loop p