[wgsl-parser] Set max recursion depth on const_expr

The `const_expr` can recurse into itself if there are type declarations
inside the const_expr (e.g. vec2<f32>(f32(1.0), f32(2.0))). Currently
there is no limit on the amount of recursion which can be triggered.

This CL sets a limit of 128 nested type declarations at which point an
error will be emitted.

Bug: chromium:1112144
Change-Id: Ifae45034dc9de35aed78ba8eddf584a46c7a55ce
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/27340
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index dcd2b44..7495b5a 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -67,6 +67,11 @@
 namespace wgsl {
 namespace {
 
+/// Controls the maximum number of times we'll call into the const_expr function
+/// from itself. This is to guard against stack overflow when there is an
+/// excessive number of type constructors inside the const_expr.
+uint32_t kMaxConstExprDepth = 128;
+
 ast::Builtin ident_to_builtin(const std::string& str) {
   if (str == "position") {
     return ast::Builtin::kPosition;
@@ -2960,7 +2965,18 @@
 //   : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
 //   | const_literal
 std::unique_ptr<ast::ConstructorExpression> ParserImpl::const_expr() {
+  return const_expr_internal(0);
+}
+
+std::unique_ptr<ast::ConstructorExpression> ParserImpl::const_expr_internal(
+    uint32_t depth) {
   auto t = peek();
+
+  if (depth > kMaxConstExprDepth) {
+    set_error(t, "max const_expr depth reached");
+    return nullptr;
+  }
+
   auto source = t.source();
 
   auto* type = type_decl();
@@ -2972,7 +2988,7 @@
     }
 
     ast::ExpressionList params;
-    auto param = const_expr();
+    auto param = const_expr_internal(depth + 1);
     if (has_error())
       return nullptr;
     if (param == nullptr) {
@@ -2987,7 +3003,7 @@
 
       next();  // Consume the peek
 
-      param = const_expr();
+      param = const_expr_internal(depth + 1);
       if (has_error())
         return nullptr;
       if (param == nullptr) {
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 28c5e33..360ad76 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -362,6 +362,9 @@
   uint32_t array_decoration_list();
   ast::type::Type* type_decl_matrix(Token t);
 
+  std::unique_ptr<ast::ConstructorExpression> const_expr_internal(
+      uint32_t depth);
+
   Context& ctx_;
   std::string error_;
   std::unique_ptr<Lexer> lexer_;
diff --git a/src/reader/wgsl/parser_impl_const_expr_test.cc b/src/reader/wgsl/parser_impl_const_expr_test.cc
index 4f4ecbc..86392a3 100644
--- a/src/reader/wgsl/parser_impl_const_expr_test.cc
+++ b/src/reader/wgsl/parser_impl_const_expr_test.cc
@@ -122,6 +122,22 @@
   EXPECT_EQ(p->error(), "1:1: unknown type alias 'invalid'");
 }
 
+TEST_F(ParserImplTest, ConstExpr_Recursion) {
+  std::stringstream out;
+  for (size_t i = 0; i < 200; i++) {
+    out << "f32(";
+  }
+  out << "1.0";
+  for (size_t i = 0; i < 200; i++) {
+    out << ")";
+  }
+  auto* p = parser(out.str());
+  auto e = p->const_expr();
+  ASSERT_TRUE(p->has_error());
+  ASSERT_EQ(e, nullptr);
+  EXPECT_EQ(p->error(), "1:517: max const_expr depth reached");
+}
+
 }  // namespace
 }  // namespace wgsl
 }  // namespace reader