Guard traversers used during parsing against stack overflow

Traversers used during parsing can be vulnerable to stack overflow
since the AST has not yet been validated for max depth. Make sure to
check for traversal depth in traversers used during parsing.

We set the maximum traversal depth in ValidateGlobalInitializer and
ValidateSwitchStatementList to 256, which matches the default value
for validating general AST complexity. The depth check is on
regardless of compiler options. In case the traversers go over the
maximum traversal depth, they fail validation.

BUG=angleproject:2453
TEST=angle_unittests

Change-Id: I89ba576e8ef69663ba35d7b9050a6da319f1757c
Reviewed-on: https://chromium-review.googlesource.com/995795
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/OutputTree.cpp b/src/compiler/translator/OutputTree.cpp
index 8d2c127..adcd242 100644
--- a/src/compiler/translator/OutputTree.cpp
+++ b/src/compiler/translator/OutputTree.cpp
@@ -31,7 +31,10 @@
 class TOutputTraverser : public TIntermTraverser
 {
   public:
-    TOutputTraverser(TInfoSinkBase &out) : TIntermTraverser(true, false, false), mOut(out) {}
+    TOutputTraverser(TInfoSinkBase &out)
+        : TIntermTraverser(true, false, false), mOut(out), mIndentDepth(0)
+    {
+    }
 
   protected:
     void visitSymbol(TIntermSymbol *) override;
@@ -52,7 +55,10 @@
     bool visitLoop(Visit visit, TIntermLoop *) override;
     bool visitBranch(Visit visit, TIntermBranch *) override;
 
+    int getCurrentIndentDepth() const { return mIndentDepth + getCurrentTraversalDepth(); }
+
     TInfoSinkBase &mOut;
+    int mIndentDepth;
 };
 
 //
@@ -79,7 +85,7 @@
 
 void TOutputTraverser::visitSymbol(TIntermSymbol *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     if (node->variable().symbolType() == SymbolType::Empty)
     {
@@ -96,7 +102,7 @@
 
 bool TOutputTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "vector swizzle (";
     node->writeOffsetsAsXYZW(&mOut);
     mOut << ")";
@@ -108,7 +114,7 @@
 
 bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     switch (node->getOp())
     {
@@ -270,7 +276,7 @@
         TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion();
         ASSERT(intermConstantUnion);
 
-        OutputTreeText(mOut, intermConstantUnion, mDepth + 1);
+        OutputTreeText(mOut, intermConstantUnion, getCurrentIndentDepth() + 1);
 
         // The following code finds the field name from the constant union
         const TConstantUnion *constantUnion   = intermConstantUnion->getConstantValue();
@@ -294,7 +300,7 @@
 
 bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     switch (node->getOp())
     {
@@ -348,22 +354,21 @@
 
 bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Function Definition:\n";
-    mOut << "\n";
     return true;
 }
 
 bool TOutputTraverser::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Invariant Declaration:\n";
     return true;
 }
 
 void TOutputTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     OutputFunction(mOut, "Function Prototype", node->getFunction());
     mOut << " (" << node->getCompleteString() << ")";
     mOut << "\n";
@@ -371,7 +376,7 @@
     for (size_t i = 0; i < paramCount; ++i)
     {
         const TVariable *param = node->getFunction()->getParam(i);
-        OutputTreeText(mOut, node, mDepth + 1);
+        OutputTreeText(mOut, node, getCurrentIndentDepth() + 1);
         mOut << "parameter: " << param->name() << " (" << param->getType().getCompleteString()
              << ")";
     }
@@ -379,7 +384,7 @@
 
 bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     if (node->getOp() == EOpNull)
     {
@@ -451,7 +456,7 @@
 
 bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Code block\n";
 
     return true;
@@ -459,7 +464,7 @@
 
 bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Declaration\n";
 
     return true;
@@ -467,18 +472,18 @@
 
 bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     mOut << "Ternary selection";
     mOut << " (" << node->getCompleteString() << ")\n";
 
-    ++mDepth;
+    ++mIndentDepth;
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Condition\n";
     node->getCondition()->traverse(this);
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     if (node->getTrueExpression())
     {
         mOut << "true case\n";
@@ -486,29 +491,29 @@
     }
     if (node->getFalseExpression())
     {
-        OutputTreeText(mOut, node, mDepth);
+        OutputTreeText(mOut, node, getCurrentIndentDepth());
         mOut << "false case\n";
         node->getFalseExpression()->traverse(this);
     }
 
-    --mDepth;
+    --mIndentDepth;
 
     return false;
 }
 
 bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     mOut << "If test\n";
 
-    ++mDepth;
+    ++mIndentDepth;
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     mOut << "Condition\n";
     node->getCondition()->traverse(this);
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     if (node->getTrueBlock())
     {
         mOut << "true case\n";
@@ -521,19 +526,19 @@
 
     if (node->getFalseBlock())
     {
-        OutputTreeText(mOut, node, mDepth);
+        OutputTreeText(mOut, node, getCurrentIndentDepth());
         mOut << "false case\n";
         node->getFalseBlock()->traverse(this);
     }
 
-    --mDepth;
+    --mIndentDepth;
 
     return false;
 }
 
 bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     mOut << "Switch\n";
 
@@ -542,7 +547,7 @@
 
 bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     if (node->getCondition() == nullptr)
     {
@@ -562,7 +567,7 @@
 
     for (size_t i = 0; i < size; i++)
     {
-        OutputTreeText(mOut, node, mDepth);
+        OutputTreeText(mOut, node, getCurrentIndentDepth());
         switch (node->getConstantValue()[i].getType())
         {
             case EbtBool:
@@ -603,16 +608,16 @@
 
 bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     mOut << "Loop with condition ";
     if (node->getType() == ELoopDoWhile)
         mOut << "not ";
     mOut << "tested first\n";
 
-    ++mDepth;
+    ++mIndentDepth;
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     if (node->getCondition())
     {
         mOut << "Loop Condition\n";
@@ -623,7 +628,7 @@
         mOut << "No loop condition\n";
     }
 
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
     if (node->getBody())
     {
         mOut << "Loop Body\n";
@@ -636,19 +641,19 @@
 
     if (node->getExpression())
     {
-        OutputTreeText(mOut, node, mDepth);
+        OutputTreeText(mOut, node, getCurrentIndentDepth());
         mOut << "Loop Terminal Expression\n";
         node->getExpression()->traverse(this);
     }
 
-    --mDepth;
+    --mIndentDepth;
 
     return false;
 }
 
 bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
 {
-    OutputTreeText(mOut, node, mDepth);
+    OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     switch (node->getFlowOp())
     {
@@ -672,9 +677,9 @@
     if (node->getExpression())
     {
         mOut << " with expression\n";
-        ++mDepth;
+        ++mIndentDepth;
         node->getExpression()->traverse(this);
-        --mDepth;
+        --mIndentDepth;
     }
     else
     {