From b78e7e320ae9224b18e125d8f979084a512edb09 Mon Sep 17 00:00:00 2001 From: Devin Jeanpierre Date: Tue, 8 Aug 2017 19:25:15 -0700 Subject: [PATCH] Add a simple fuzz test for jsoncpp. The idea is to then add it to the google oss-fuzz project and get jsoncpp regularly tested for bugs. https://github.com/google/oss-fuzz/ I've followed the instructions at: https://github.com/google/oss-fuzz/blob/master/docs/ideal_integration.md Tested using a simple oss-fuzz setup and with the modified jsoncpp unit tests (cmake . && make). I don't really know git, so also I've royally screwed up the commit history, and artificially flattened it into this one commit. :) --- AUTHORS | 1 + src/test_lib_json/CMakeLists.txt | 2 ++ src/test_lib_json/fuzz.cpp | 57 ++++++++++++++++++++++++++++++++ src/test_lib_json/fuzz.h | 14 ++++++++ src/test_lib_json/main.cpp | 16 +++++++++ 5 files changed, 90 insertions(+) create mode 100644 src/test_lib_json/fuzz.cpp create mode 100644 src/test_lib_json/fuzz.h diff --git a/AUTHORS b/AUTHORS index 5747e61c1..93169a829 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,6 +37,7 @@ datadiode David Seifert David West dawesc +Devin Jeanpierre Dmitry Marakasov dominicpezzuto Don Milham diff --git a/src/test_lib_json/CMakeLists.txt b/src/test_lib_json/CMakeLists.txt index 7000264a7..7f19a2480 100644 --- a/src/test_lib_json/CMakeLists.txt +++ b/src/test_lib_json/CMakeLists.txt @@ -3,6 +3,8 @@ ADD_EXECUTABLE( jsoncpp_test jsontest.cpp jsontest.h + fuzz.cpp + fuzz.h main.cpp ) diff --git a/src/test_lib_json/fuzz.cpp b/src/test_lib_json/fuzz.cpp new file mode 100644 index 000000000..f70a1547c --- /dev/null +++ b/src/test_lib_json/fuzz.cpp @@ -0,0 +1,57 @@ +// Copyright 2007-2010 The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#include "fuzz.h" + +#include +#include +#include +#include +#include + +namespace { +// https://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time +uint32_t JenkinsOneAtATimeHash(const uint8_t* data, size_t size) { + uint32_t hash = 0; + for (size_t i = 0; i < size; i++) { + hash += data[i]; + hash += hash << 10; + hash ^= hash >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; +} +} // namespace + +namespace Json { +class Exception; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + Json::CharReaderBuilder builder; + + uint32_t hash_settings = JenkinsOneAtATimeHash(data, size); + builder.settings_["failIfExtra"] = hash_settings & (1 << 0); + builder.settings_["allowComments_"] = hash_settings & (1 << 1); + builder.settings_["strictRoot_"] = hash_settings & (1 << 2); + builder.settings_["allowDroppedNullPlaceholders_"] = hash_settings & (1 << 3); + builder.settings_["allowNumericKeys_"] = hash_settings & (1 << 4); + builder.settings_["allowSingleQuotes_"] = hash_settings & (1 << 5); + builder.settings_["failIfExtra_"] = hash_settings & (1 << 6); + builder.settings_["rejectDupKeys_"] = hash_settings & (1 << 7); + builder.settings_["allowSpecialFloats_"] = hash_settings & (1 << 8); + + std::unique_ptr reader(builder.newCharReader()); + + Json::Value root; + const char* data_str = reinterpret_cast(data); + try { + reader->parse(data_str, data_str + size, &root, nullptr); + } catch (Json::Exception const&) {} + // Whether it succeeded or not doesn't matter. + return 0; +} diff --git a/src/test_lib_json/fuzz.h b/src/test_lib_json/fuzz.h new file mode 100644 index 000000000..90b49b3b4 --- /dev/null +++ b/src/test_lib_json/fuzz.h @@ -0,0 +1,14 @@ +// Copyright 2007-2010 The JsonCpp Authors +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef FUZZ_H_INCLUDED +#define FUZZ_H_INCLUDED + +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +#endif // ifndef FUZZ_H_INCLUDED \ No newline at end of file diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp index 44f6591ed..a162482b4 100644 --- a/src/test_lib_json/main.cpp +++ b/src/test_lib_json/main.cpp @@ -4,6 +4,7 @@ // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #include "jsontest.h" +#include "fuzz.h" #include #include #include @@ -2510,6 +2511,19 @@ JSONTEST_FIXTURE(RValueTest, moveConstruction) { #endif } +struct FuzzTest : JsonTest::TestCase {}; + +// Build and run the fuzz test without any fuzzer, so that it's guaranteed not +// go out of date, even if it's never run as an actual fuzz test. +JSONTEST_FIXTURE(FuzzTest, fuzzDoesntCrash) { + const std::string example = "{}"; + JSONTEST_ASSERT_EQUAL( + 0, + LLVMFuzzerTestOneInput( + reinterpret_cast(example.c_str()), + example.size())); +} + int main(int argc, const char* argv[]) { JsonTest::Runner runner; JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); @@ -2585,5 +2599,7 @@ int main(int argc, const char* argv[]) { JSONTEST_REGISTER_FIXTURE(runner, RValueTest, moveConstruction); + JSONTEST_REGISTER_FIXTURE(runner, FuzzTest, fuzzDoesntCrash); + return runner.runCommandLine(argc, argv); }