From 42ca58d3a933d7613410135ac558fe0a9db8d3ad Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Thu, 20 Apr 2023 13:31:08 +0200 Subject: [PATCH] Throw, if `BlockSyntax.parseLines` loops indefinitely We throw an `AssertionError` if `BlockSyntax.parseLines` enters an infinite loop. This should have no effect, if there are no bugs. But if there is a bug, it's easier to discover it in production if the failure mode isn't an infinite loop eating up CPU, but just an unhandled exception. --- lib/src/block_parser.dart | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/src/block_parser.dart b/lib/src/block_parser.dart index fc0c1ac5..efe2352c 100644 --- a/lib/src/block_parser.dart +++ b/lib/src/block_parser.dart @@ -164,7 +164,9 @@ class BlockParser { // number of cells, which makes a table like structure not be recognized. BlockSyntax? neverMatch; + var iterationsWithoutProgress = 0; while (!isDone) { + final positionBefore = _pos; for (final syntax in blockSyntaxes) { if (neverMatch == syntax) { continue; @@ -173,7 +175,6 @@ class BlockParser { if (syntax.canParse(this)) { _previousSyntax = _currentSyntax; _currentSyntax = syntax; - final positionBefore = _pos; final block = syntax.parse(this); if (block != null) { blocks.add(block); @@ -189,6 +190,25 @@ class BlockParser { break; } } + // Count the number of iterations without progress. + // This ensures that we don't have an infinite loop. And if we have an + // infinite loop, it's easier to gracefully recover from an error, than + // it is to discover an kill an isolate that's stuck in an infinite loop. + // Technically, it should be perfectly safe to remove this check + // But as it's possible to inject custom BlockSyntax implementations and + // combine existing ones, it is hard to promise that no combination can't + // trigger an infinite loop + if (positionBefore == _pos) { + iterationsWithoutProgress++; + if (iterationsWithoutProgress > 2) { + // If this happens we throw an error to avoid having the parser + // running in an infinite loop. An error is easier to handle. + // If you see this error in production please file a bug! + throw AssertionError('BlockParser.parseLines is not advancing'); + } + } else { + iterationsWithoutProgress = 0; + } } return blocks;