preprocessor: add a limit to the number of token expanded

BUG=angleproject:1522
BUG=chromium:648074

Change-Id: Ibf0858aaeb81933dd221ac82a49160169b48a495
Reviewed-on: https://chromium-review.googlesource.com/387211
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/preprocessor/MacroExpander.cpp b/src/compiler/preprocessor/MacroExpander.cpp
index 51b69f8..a4519f2 100644
--- a/src/compiler/preprocessor/MacroExpander.cpp
+++ b/src/compiler/preprocessor/MacroExpander.cpp
@@ -15,6 +15,11 @@
 namespace pp
 {
 
+namespace
+{
+
+const size_t kMaxContextTokens = 10000;
+
 class TokenLexer : public Lexer
 {
  public:
@@ -46,8 +51,10 @@
     TokenVector::const_iterator mIter;
 };
 
+}  // anonymous namespace
+
 MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics)
-    : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics)
+    : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mTotalTokensInContexts(0)
 {
 }
 
@@ -114,6 +121,7 @@
     }
     else
     {
+        assert(mTotalTokensInContexts == 0);
         mLexer->lex(token);
     }
 }
@@ -164,6 +172,7 @@
     context->macro = &macro;
     context->replacements.swap(replacements);
     mContextStack.push_back(context);
+    mTotalTokensInContexts += context->replacements.size();
     return true;
 }
 
@@ -179,6 +188,7 @@
     assert(context->macro->expansionCount > 0);
     context->macro->disabled = false;
     context->macro->expansionCount--;
+    mTotalTokensInContexts -= context->replacements.size();
     delete context;
 }
 
@@ -321,6 +331,7 @@
     // Pre-expand each argument before substitution.
     // This step expands each argument individually before they are
     // inserted into the macro body.
+    size_t numTokens = 0;
     for (std::size_t i = 0; i < args->size(); ++i)
     {
         MacroArg &arg = args->at(i);
@@ -333,6 +344,12 @@
         {
             arg.push_back(token);
             expander.lex(&token);
+            numTokens++;
+            if (numTokens + mTotalTokensInContexts > kMaxContextTokens)
+            {
+                mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+                return false;
+            }
         }
     }
     return true;
@@ -344,6 +361,14 @@
 {
     for (std::size_t i = 0; i < macro.replacements.size(); ++i)
     {
+        if (!replacements->empty() &&
+            replacements->size() + mTotalTokensInContexts > kMaxContextTokens)
+        {
+            const Token &token = replacements->back();
+            mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+            return;
+        }
+
         const Token &repl = macro.replacements[i];
         if (repl.type != Token::IDENTIFIER)
         {
diff --git a/src/compiler/preprocessor/MacroExpander.h b/src/compiler/preprocessor/MacroExpander.h
index dbf9803..5e5dbe9 100644
--- a/src/compiler/preprocessor/MacroExpander.h
+++ b/src/compiler/preprocessor/MacroExpander.h
@@ -84,6 +84,7 @@
 
     std::unique_ptr<Token> mReserveToken;
     std::vector<MacroContext *> mContextStack;
+    size_t mTotalTokensInContexts;
 };
 
 }  // namespace pp