Fix infinite spin in wgsl parser

If the parser hits a `maximum parser recursive depth reached` situation, then we need to try and resynchronize the parser.

If we fail to do this, then the synchronized_ flag may remain true, and the parser will believe progress is still being made.
In this situation the parser may try to reparse the same token, forever.

By calling sync_to() we either find the end of the block, and forward progress can be made, or synchronized_ is set to false, and the parser can error out cleanly.

Add test case from fuzzer report.

Fixed: chromium:1180128
Change-Id: I893077677fd3dfbd4b9b400cd32db842b06db500
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/42029
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 37098ed..61e64b5 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1036,6 +1036,7 @@
     "src/reader/wgsl/parser_impl_assignment_stmt_test.cc",
     "src/reader/wgsl/parser_impl_body_stmt_test.cc",
     "src/reader/wgsl/parser_impl_break_stmt_test.cc",
+    "src/reader/wgsl/parser_impl_bug_cases_test.cc",
     "src/reader/wgsl/parser_impl_call_stmt_test.cc",
     "src/reader/wgsl/parser_impl_case_body_test.cc",
     "src/reader/wgsl/parser_impl_const_expr_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2704dcb..885d0bd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -567,6 +567,7 @@
       reader/wgsl/parser_impl_assignment_stmt_test.cc
       reader/wgsl/parser_impl_body_stmt_test.cc
       reader/wgsl/parser_impl_break_stmt_test.cc
+      reader/wgsl/parser_impl_bug_cases_test.cc
       reader/wgsl/parser_impl_call_stmt_test.cc
       reader/wgsl/parser_impl_case_body_test.cc
       reader/wgsl/parser_impl_const_expr_test.cc
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 727603a..94ffb5a 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -3162,7 +3162,15 @@
 template <typename F, typename T>
 T ParserImpl::sync(Token::Type tok, F&& body) {
   if (sync_depth_ >= kMaxSyncDepth) {
-    return add_error(peek(), "maximum parser recursive depth reached");
+    // We've hit a maximum parser recursive depth.
+    // We can't call into body() as we might stack overflow.
+    // Instead, report an error...
+    add_error(peek(), "maximum parser recursive depth reached");
+    // ...and try to resynchronize. If we cannot resynchronize to `tok` then
+    // synchronized_ is set to false, and the parser knows that forward progress
+    // is not being made.
+    sync_to(tok, /* consume: */ true);
+    return Failure::kErrored;
   }
 
   sync_tokens_.push_back(tok);
diff --git a/src/reader/wgsl/parser_impl_bug_cases_test.cc b/src/reader/wgsl/parser_impl_bug_cases_test.cc
new file mode 100644
index 0000000..891c682
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_bug_cases_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/ast/break_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+TEST_F(ParserImplTest, Bug_chromium_1180130) {
+  auto p = parser(
+      R"(a;{}}a;}};{{{;{}};{};{}}a;{}};{{{}};{{{;{}};{};{}}a;{}};{{{}}{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{{{;u[([[,a;{}}a;{}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{z{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}}{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}}i;{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};{}{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{{;u[({}};{{{}};{{}a;{}};{{{}};{{{;{}};{};}a;{}};{{{}};{{;u[[a,([}};{{{;{}})");
+  EXPECT_FALSE(p->Parse());
+  EXPECT_TRUE(p->has_error());
+}
+
+}  // namespace
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint