Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simple fuzz test for jsoncpp. #610

Merged
merged 2 commits into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ datadiode <jochen.neubeck@vodafone.de>
David Seifert <soap@gentoo.org>
David West <david-west@idexx.com>
dawesc <chris.dawes@eftlab.co.uk>
Devin Jeanpierre <jeanpierreda@google.com>
Dmitry Marakasov <amdmi3@amdmi3.ru>
dominicpezzuto <dom@dompezzuto.com>
Don Milham <dmilham@gmail.com>
Expand Down
2 changes: 2 additions & 0 deletions src/test_lib_json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
ADD_EXECUTABLE( jsoncpp_test
jsontest.cpp
jsontest.h
fuzz.cpp
fuzz.h
main.cpp
)

Expand Down
57 changes: 57 additions & 0 deletions src/test_lib_json/fuzz.cpp
Original file line number Diff line number Diff line change
@@ -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 <json/config.h>
#include <json/json.h>
#include <memory>
#include <string>
#include <stdint.h>

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than hashing the input, it's simpler to just use the first byte or two for configuring options. We can early return 0 if size < REQUIRED_BYTES_FOR_CONFIG, and the fuzzer will figure out not to follow this edge pretty quickly.

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<Json::CharReader> reader(builder.newCharReader());

Json::Value root;
const char* data_str = reinterpret_cast<const char*>(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;
}
14 changes: 14 additions & 0 deletions src/test_lib_json/fuzz.h
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>
#include <stdint.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

#endif // ifndef FUZZ_H_INCLUDED
16 changes: 16 additions & 0 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE

#include "jsontest.h"
#include "fuzz.h"
#include <json/config.h>
#include <json/json.h>
#include <cstring>
Expand Down Expand Up @@ -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<const uint8_t*>(example.c_str()),
example.size()));
}

int main(int argc, const char* argv[]) {
JsonTest::Runner runner;
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
Expand Down Expand Up @@ -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);
}