[jsoncpp] Add fuzzer.

Bug: 950091
Change-Id: I84a42cb4d4fbc853e8eb3889b85f7b6fbecbb10d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1598236
Auto-Submit: Jonathan Metzman <metzman@chromium.org>
Reviewed-by: Sam Clegg <sbc@chromium.org>
Commit-Queue: Jonathan Metzman <metzman@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#658229}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: acb957aaf9190df9e6f26f3e9783fa6b66b6fe4a
diff --git a/BUILD.gn b/BUILD.gn
index cf40895..739f54f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
+
 config("jsoncpp_config") {
   include_dirs = [
     "overrides/include",
@@ -33,3 +35,13 @@
 
   include_dirs = [ "source/src/lib_json" ]
 }
+
+fuzzer_test("jsoncpp_fuzzer") {
+  sources = [
+    "fuzzers/json_fuzzer.cc",
+  ]
+  deps = [
+    ":jsoncpp",
+  ]
+  dict = "//testing/libfuzzer/fuzzers/dicts/json.dict"
+}
diff --git a/fuzzers/json_fuzzer.cc b/fuzzers/json_fuzzer.cc
new file mode 100644
index 0000000..c587567
--- /dev/null
+++ b/fuzzers/json_fuzzer.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 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.
+//
+// JsonCpp fuzzing wrapper to help with automated fuzz testing.
+
+#include <stdint.h>
+#include <climits>
+#include <cstdio>
+#include <memory>
+#include "third_party/jsoncpp/source/include/json/json.h"
+
+// JsonCpp has a few different parsing options. The code below makes sure that
+// the most intersting variants are tested.
+enum {
+  kFeatureSetAll = 0,
+  kFeatureSetDefault = 1,
+  kFeatureSetStrict = 2,
+  kFeatureSetLast
+};
+const int kSizeOfFeatureSet = kFeatureSetLast;
+
+namespace {
+struct Common {
+  Json::Features features[kSizeOfFeatureSet];
+};
+}  // namespace
+
+static Common* Initialize() {
+  static Common common;
+  common.features[kFeatureSetAll].allowComments_ = true;
+  common.features[kFeatureSetAll].strictRoot_ = false;
+
+  common.features[kFeatureSetStrict] = Json::Features::strictMode();
+
+  return &common;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static Common* common = Initialize();
+
+  for (int i = 0; i < kSizeOfFeatureSet; ++i) {
+    // Parse Json.
+    Json::Reader reader(common->features[i]);
+    Json::Value root;
+    bool res = reader.parse(reinterpret_cast<const char*>(data),
+                            reinterpret_cast<const char*>(data + size), root,
+                            /*collectComments=*/true);
+    if (!res) {
+      continue;
+    }
+
+    // Write and re-read json.
+    Json::FastWriter writer;
+    std::string output_json = writer.write(root);
+
+    Json::Value root_again;
+    res = reader.parse(output_json, root_again, /*collectComments=*/true);
+    if (!res) {
+      continue;
+    }
+
+    // Run equality test.
+    // Note: This actually causes the Json::Value tree to be traversed and all
+    // the values to be dereferenced (until two of them are found not equal),
+    // which is great for detecting memory corruption bugs when compiled with
+    // AddressSanitizer. The result of the comparison is ignored, as it is
+    // expected that both the original and the re-read version will differ from
+    // time to time (e.g. due to floating point accuracy loss).
+    (void)(root == root_again);
+  }
+
+  return 0;
+}