Easier minimization for SQLite LPM Fuzzer

Adds support for turning off specific queries with an environment
variable; makes it very easy to rule out a ton of irrelevant queries
in a very short amount of time when minimizing a test case.

R=metzman@chromium.org, pwnall@chromium.org

Bug: 909886
Change-Id: Ia785a8d89cad415f791fd28cd498244358437898
Reviewed-on: https://chromium-review.googlesource.com/c/1388035
Reviewed-by: Jonathan Metzman <metzman@chromium.org>
Reviewed-by: Victor Costan <pwnall@chromium.org>
Commit-Queue: Matthew Denton <mpdenton@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#620992}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 55a46db4d17608aca41a7016557d5cea064ae126
diff --git a/BUILD.gn b/BUILD.gn
index 2f89a7b..50dbf8e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -432,6 +432,8 @@
 
 source_set("sqlite3_lpm_fuzzer_core") {
   sources = [
+    "fuzz/disabled_queries_parser.cc",
+    "fuzz/disabled_queries_parser.h",
     "fuzz/sql_run_queries.cc",
     "fuzz/sql_run_queries.h",
   ]
diff --git a/fuzz/disabled_queries_parser.cc b/fuzz/disabled_queries_parser.cc
new file mode 100644
index 0000000..c2bd17b
--- /dev/null
+++ b/fuzz/disabled_queries_parser.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/sqlite/fuzz/disabled_queries_parser.h"
+
+namespace sql_fuzzer {
+
+std::set<std::string> ParseDisabledQueries(std::string query_list) {
+  // Trimming
+  query_list.erase(query_list.find_last_not_of(" \t\n\r\f\v") + 1);
+  query_list.erase(0, query_list.find_first_not_of(" \t\n\r\f\v"));
+  std::set<std::string> ret;
+  std::string curr_query;
+  for (size_t i = 0; i < query_list.length(); i++) {
+    if (query_list[i] == ',') {
+      ret.insert(curr_query);
+      curr_query.clear();
+      continue;
+    }
+    curr_query += query_list[i];
+  }
+  if (curr_query.length() != 0) {
+    // Add last query, which doesn't have a trailing comma
+    ret.insert(curr_query);
+  }
+  return ret;
+}
+
+}  // namespace sql_fuzzer
diff --git a/fuzz/disabled_queries_parser.h b/fuzz/disabled_queries_parser.h
new file mode 100644
index 0000000..665b871
--- /dev/null
+++ b/fuzz/disabled_queries_parser.h
@@ -0,0 +1,16 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_SQLITE_FUZZ_DISABLED_QUERIES_PARSER_H_
+#define THIRD_PARTY_SQLITE_FUZZ_DISABLED_QUERIES_PARSER_H_
+
+#include <set>
+#include <string>
+
+namespace sql_fuzzer {
+// |query_list| should be a list of disabled queries separated only by commas.
+std::set<std::string> ParseDisabledQueries(std::string query_list);
+}  // namespace sql_fuzzer
+
+#endif  // THIRD_PARTY_SQLITE_FUZZ_DISABLED_QUERIES_PARSER_H_
diff --git a/fuzz/sql_fuzzer.cc b/fuzz/sql_fuzzer.cc
index 7003a31..8ea762b 100644
--- a/fuzz/sql_fuzzer.cc
+++ b/fuzz/sql_fuzzer.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "testing/libfuzzer/proto/lpm_interface.h"
+#include "third_party/sqlite/fuzz/disabled_queries_parser.h"
 #include "third_party/sqlite/fuzz/sql_query_grammar.pb.h"
 #include "third_party/sqlite/fuzz/sql_query_proto_to_string.h"
 #include "third_party/sqlite/fuzz/sql_run_queries.h"
@@ -34,6 +35,12 @@
 // 5. Temp-file database, for better fuzzing of VACUUM and journalling.
 
 DEFINE_BINARY_PROTO_FUZZER(const SQLQueries& sql_queries) {
+  char* skip_queries = getenv("SQL_SKIP_QUERIES");
+  if (skip_queries) {
+    sql_fuzzer::SetDisabledQueries(
+        sql_fuzzer::ParseDisabledQueries(skip_queries));
+  }
+
   std::vector<std::string> queries = sql_fuzzer::SQLQueriesToVec(sql_queries);
 
   if (getenv("LPM_DUMP_NATIVE_INPUT") && queries.size() != 0) {
diff --git a/fuzz/sql_multithreaded_fuzzer.cc b/fuzz/sql_multithreaded_fuzzer.cc
index 0f7ff82..e4162c6 100644
--- a/fuzz/sql_multithreaded_fuzzer.cc
+++ b/fuzz/sql_multithreaded_fuzzer.cc
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "testing/libfuzzer/proto/lpm_interface.h"
+#include "third_party/sqlite/fuzz/disabled_queries_parser.h"
 #include "third_party/sqlite/fuzz/sql_query_grammar.pb.h"
 #include "third_party/sqlite/fuzz/sql_query_proto_to_string.h"
 #include "third_party/sqlite/fuzz/sql_run_queries.h"
@@ -25,6 +26,12 @@
 }
 
 DEFINE_BINARY_PROTO_FUZZER(const MultipleSQLQueries& multiple_sql_queries) {
+  char* skip_queries = getenv("SQL_SKIP_QUERIES");
+  if (skip_queries) {
+    sql_fuzzer::SetDisabledQueries(
+        sql_fuzzer::ParseDisabledQueries(skip_queries));
+  }
+
   assert(multiple_sql_queries.GetDescriptor()->field_count() == kNumThreads);
 
   sqlite3* db = sql_fuzzer::InitConnectionForFuzzing();
diff --git a/fuzz/sql_query_proto_to_string.cc b/fuzz/sql_query_proto_to_string.cc
index ef5d7e1..84a09fd 100644
--- a/fuzz/sql_query_proto_to_string.cc
+++ b/fuzz/sql_query_proto_to_string.cc
@@ -21,6 +21,10 @@
 
 #define CONV_FN(TYPE, VAR_NAME) std::string TYPE##ToString(const TYPE& VAR_NAME)
 
+#define RETURN_IF_DISABLED_QUERY(TYPE)     \
+  if (disabled_queries_.count(#TYPE) != 0) \
+    return "";
+
 namespace sql_fuzzer {
 
 namespace {
@@ -49,6 +53,8 @@
 
 constexpr uint32_t kMaxViewNumber = 5;
 constexpr uint32_t kMaxTriggerNumber = 10;
+
+std::set<std::string> disabled_queries_;
 }  // namespace
 
 CONV_FN(Expr, expr);
@@ -1169,6 +1175,7 @@
 }
 
 CONV_FN(CreateTable, create_table) {
+  RETURN_IF_DISABLED_QUERY(CreateTable);
 #if defined(FUZZ_FTS3)
   return "";  // Don't create normal tables in FTS3 fuzzing mode.
 #endif
@@ -1346,6 +1353,7 @@
 }
 
 CONV_FN(Insert, insert) {
+  RETURN_IF_DISABLED_QUERY(Insert);
   std::string ret;
   if (insert.has_with()) {
     ret += WithStatementToString(insert.with());
@@ -1400,6 +1408,7 @@
 }
 
 CONV_FN(Delete, delete_) {
+  RETURN_IF_DISABLED_QUERY(Delete);
   std::string ret;
   if (delete_.has_with()) {
     ret += WithStatementToString(delete_.with());
@@ -1417,6 +1426,7 @@
 // ~~~~UPDATE~~~~
 // WARNING no space at end
 CONV_FN(Update, update) {
+  RETURN_IF_DISABLED_QUERY(Update);
   std::string ret;
   if (update.has_with()) {
     ret += WithStatementToString(update.with());
@@ -1755,6 +1765,7 @@
 }
 
 CONV_FN(Select, select) {
+  RETURN_IF_DISABLED_QUERY(Select);
   std::string ret;
   if (select.has_with()) {
     ret += WithStatementToString(select.with());
@@ -1873,6 +1884,7 @@
 }
 
 CONV_FN(FTS3SpecialCommand, fsc) {
+  RETURN_IF_DISABLED_QUERY(FTS3SpecialCommand);
   std::string ret("INSERT INTO ");
   ret += FTS3TableToString(fsc.table());
   ret += "(";
@@ -1905,6 +1917,7 @@
 
 // WARNING no space at end
 CONV_FN(FTS3SelectMatch, fsm) {
+  RETURN_IF_DISABLED_QUERY(FTS3SelectMatch);
   std::string ret("SELECT * FROM ");
   ret += FTS3TableToString(fsm.table());
   ret += " WHERE ";
@@ -1915,6 +1928,7 @@
 }
 
 CONV_FN(FTS3SpecificQuery, fsq) {
+  RETURN_IF_DISABLED_QUERY(FTS3SpecificQuery);
 #if defined(FUZZ_FTS3)
   // oneof
   if (fsq.has_command()) {
@@ -1943,6 +1957,7 @@
 }
 
 CONV_FN(CreateFTS3Table, cft) {
+  RETURN_IF_DISABLED_QUERY(CreateFTS3Table);
   std::string ret("CREATE VIRTUAL TABLE ");
   if (cft.if_not_exists())
     ret += "IF NOT EXISTS ";
@@ -2067,6 +2082,7 @@
 }
 
 CONV_FN(FTS3HiddenTableInsert, fi) {
+  RETURN_IF_DISABLED_QUERY(FTS3HiddenTableInsert);
   std::string ret("INSERT INTO ");
   ret += FTS3HiddenTableToString(fi.fht());
   if (fi.col_vals_size() == 0) {
@@ -2090,6 +2106,7 @@
 }
 
 CONV_FN(FTS3HiddenTableUpdate, fu) {
+  RETURN_IF_DISABLED_QUERY(FTS3HiddenTableUpdate);
   std::string ret("UPDATE ");
   ret += FTS3HiddenTableToString(fu.fht());
   ret += " ";
@@ -2117,6 +2134,7 @@
 }
 
 CONV_FN(FTS3HiddenTableDelete, fd) {
+  RETURN_IF_DISABLED_QUERY(FTS3HiddenTableDelete);
   std::string ret("DELETE FROM ");
   ret += FTS3HiddenTableToString(fd.fht());
   if (fd.has_col_where()) {
@@ -2130,6 +2148,7 @@
 
 // ~~~~TRANSACTIONS/SAVEPOINTS
 CONV_FN(BeginTransaction, bt) {
+  RETURN_IF_DISABLED_QUERY(BeginTransaction);
   std::string ret("BEGIN ");
   if (bt.has_type()) {
     ret += BeginTransaction_TransactionType_Name(bt.type());
@@ -2140,11 +2159,13 @@
 }
 
 CONV_FN(CommitTransaction, ct) {
+  RETURN_IF_DISABLED_QUERY(CommitTransaction);
   return EnumStrReplaceUnderscores(
       CommitTransaction_CommitText_Name(ct.text()));
 }
 
 CONV_FN(RollbackStatement, rt) {
+  RETURN_IF_DISABLED_QUERY(RollbackStatement);
 #if !defined(FUZZ_OMIT_SAVEPOINT)
   if (rt.has_save_point()) {
     return "ROLLBACK TO SAVEPOINT " + SavePointToString(rt.save_point());
@@ -2155,15 +2176,18 @@
 
 #if !defined(FUZZ_OMIT_SAVEPOINT)
 CONV_FN(CreateSavePoint, csp) {
+  RETURN_IF_DISABLED_QUERY(CreateSavePoint);
   return "SAVEPOINT " + SavePointToString(csp.save_point());
 }
 
 CONV_FN(ReleaseSavePoint, rsp) {
+  RETURN_IF_DISABLED_QUERY(ReleaseSavePoint);
   return "RELEASE SAVEPOINT " + SavePointToString(rsp.save_point());
 }
 #endif
 
 CONV_FN(Analyze, a) {
+  RETURN_IF_DISABLED_QUERY(Analyze);
   std::string ret("ANALYZE");
   if (a.has_schema_name()) {
     ret += " ";
@@ -2188,6 +2212,7 @@
 
 // ~~~~VACUUM~~~~
 CONV_FN(Vacuum, v) {
+  RETURN_IF_DISABLED_QUERY(Vacuum);
   std::string ret("VACUUM");
   if (v.has_schema()) {
     ret += " ";
@@ -2198,6 +2223,7 @@
 
 // ~~~~PRAGMA~~~~
 CONV_FN(Pragma, p) {
+  RETURN_IF_DISABLED_QUERY(Pragma);
 #if defined(FUZZ_OMIT_PRAGMA)
   return "";
 #else
@@ -2255,6 +2281,7 @@
 
 // ~~~~CREATE INDEX~~~~
 CONV_FN(CreateIndex, ci) {
+  RETURN_IF_DISABLED_QUERY(CreateIndex);
   std::string ret("CREATE ");
   if (ci.unique())
     ret += "UNIQUE ";
@@ -2280,6 +2307,7 @@
 
 // ~~~~CREATE VIEW~~~~
 CONV_FN(CreateView, cv) {
+  RETURN_IF_DISABLED_QUERY(CreateView);
   std::string ret("CREATE ");
   if (cv.has_temp_modifier()) {
     ret += EnumStrReplaceUnderscores(TempModifier_Name(cv.temp_modifier()))
@@ -2321,6 +2349,7 @@
 
 // WARNING no space at end
 CONV_FN(CreateTrigger, ct) {
+  RETURN_IF_DISABLED_QUERY(CreateTrigger);
   std::string ret("CREATE ");
   if (ct.has_temp_modifier()) {
     ret += EnumStrReplaceUnderscores(TempModifier_Name(ct.temp_modifier()))
@@ -2375,6 +2404,7 @@
 
 // ~~~~REINDEX~~~~
 CONV_FN(ReIndex, ri) {
+  RETURN_IF_DISABLED_QUERY(ReIndex);
 // Chrome doesn't use REINDEX
 #if !defined(SQLITE_OMIT_REINDEX)
   if (ri.empty())
@@ -2400,6 +2430,7 @@
 }
 
 CONV_FN(Drop, d) {
+  RETURN_IF_DISABLED_QUERY(Drop);
   std::string ret("DROP ");
   std::string if_exists("");
   std::string schema("");
@@ -2436,6 +2467,7 @@
 
 // ~~~~ALTER TABLE~~~~
 CONV_FN(AlterTable, at) {
+  RETURN_IF_DISABLED_QUERY(AlterTable);
   std::string ret("ALTER TABLE ");
   ret += ExprSchemaTableToString(at.schema_table());
   ret += " ";
@@ -2460,6 +2492,7 @@
 
 // ~~~~ATTACH DATABASE~~~~
 CONV_FN(AttachDatabase, ad) {
+  RETURN_IF_DISABLED_QUERY(AttachDatabase);
   std::string ret("ATTACH DATABASE \'");
   if (ad.in_memory()) {
     if (ad.file_uri()) {
@@ -2487,6 +2520,7 @@
 
 // ~~~~DETACH DATABASE~~~~
 CONV_FN(DetachDatabase, dd) {
+  RETURN_IF_DISABLED_QUERY(DetachDatabase);
   std::string ret("DETACH DATABASE ");
   ret += SchemaToString(dd.schema());
   return ret;
@@ -2716,4 +2750,8 @@
   return queries;
 }
 
+void SetDisabledQueries(std::set<std::string> disabled_queries) {
+  disabled_queries_ = disabled_queries;
+}
+
 }  // namespace sql_fuzzer
diff --git a/fuzz/sql_query_proto_to_string.h b/fuzz/sql_query_proto_to_string.h
index 4664d74..b4b7fe9 100644
--- a/fuzz/sql_query_proto_to_string.h
+++ b/fuzz/sql_query_proto_to_string.h
@@ -23,6 +23,8 @@
 
 std::string SQLQueryToString(const sql_query_grammar::SQLQuery&);
 
+void SetDisabledQueries(std::set<std::string> disabled_queries);
+
 }  // namespace sql_fuzzer
 
 #endif  // THIRD_PARTY_SQLITE_FUZZ_SQL_QUERY_PROTO_TO_STRING_H_