[GN fuzzer] Stack overflow fix.
Fuzzathon 2017.
Bug: 648076,749793,773426,768111,754972,734401,734200
Change-Id: Ic608c5a374252809443a879ad4e2ddf8f6184697
Reviewed-on: https://chromium-review.googlesource.com/736159
Commit-Queue: Penny MacNeil <pennymac@chromium.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Reviewed-by: Max Moroz <mmoroz@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#512626}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 697f302d91e7ad4466416ab22c290c7fea9c6b93
diff --git a/tools/gn/parser_fuzzer.cc b/tools/gn/parser_fuzzer.cc
index c7b4325..e40da78 100644
--- a/tools/gn/parser_fuzzer.cc
+++ b/tools/gn/parser_fuzzer.cc
@@ -9,6 +9,51 @@
#include "tools/gn/source_file.h"
#include "tools/gn/tokenizer.h"
+namespace {
+
+enum { kMaxContentDepth = 256, kMaxDodgy = 256 };
+
+// Some auto generated input is too unreasonable for fuzzing GN.
+// We see stack overflow when the parser hits really deeply "nested" input.
+// (I.E.: certain input that causes nested parsing function calls).
+//
+// Abstract max limits are undesirable in the release GN code, so some sanity
+// checks in the fuzzer to prevent stack overflow are done here.
+// - 1) Too many opening bracket, paren, or brace in a row.
+// - 2) Too many '!', '<' or '>' operators in a row.
+bool SanityCheckContent(const std::vector<Token>& tokens) {
+ int depth = 0;
+ int dodgy_count = 0;
+ for (const auto& token : tokens) {
+ switch (token.type()) {
+ case Token::LEFT_PAREN:
+ case Token::LEFT_BRACKET:
+ case Token::LEFT_BRACE:
+ ++depth;
+ break;
+ case Token::RIGHT_PAREN:
+ case Token::RIGHT_BRACKET:
+ case Token::RIGHT_BRACE:
+ --depth;
+ break;
+ case Token::BANG:
+ case Token::LESS_THAN:
+ case Token::GREATER_THAN:
+ ++dodgy_count;
+ break;
+ default:
+ break;
+ }
+ // Bail out as soon as a boundary is hit, inside the loop.
+ if (depth >= kMaxContentDepth || dodgy_count >= kMaxDodgy)
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
SourceFile source;
InputFile input(source);
@@ -16,6 +61,8 @@
Err err;
std::vector<Token> tokens = Tokenizer::Tokenize(&input, &err);
+ if (!SanityCheckContent(tokens))
+ return 0;
if (!err.has_error())
Parser::Parse(tokens, &err);