Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 24 additions & 11 deletions src/test_lib_json/fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "fuzz.h"

#include <cstdint>
#include <cstdlib>
#include <json/config.h>
#include <json/json.h>
#include <memory>
Expand All @@ -25,17 +26,29 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
data += sizeof(uint32_t);
size -= sizeof(uint32_t);

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);
builder.settings_["collectComments"] = hash_settings & (1 << 9);
builder.settings_["allowTrailingCommas_"] = hash_settings & (1 << 10);
builder.settings_["failIfExtra"] = (hash_settings & (1 << 0)) != 0;
builder.settings_["allowComments"] = (hash_settings & (1 << 1)) != 0;
builder.settings_["strictRoot"] = (hash_settings & (1 << 2)) != 0;
builder.settings_["allowDroppedNullPlaceholders"] =
(hash_settings & (1 << 3)) != 0;
builder.settings_["allowNumericKeys"] = (hash_settings & (1 << 4)) != 0;
builder.settings_["allowSingleQuotes"] = (hash_settings & (1 << 5)) != 0;
builder.settings_["skipBom"] = (hash_settings & (1 << 6)) != 0;
builder.settings_["rejectDupKeys"] = (hash_settings & (1 << 7)) != 0;
builder.settings_["allowSpecialFloats"] = (hash_settings & (1 << 8)) != 0;
builder.settings_["collectComments"] = (hash_settings & (1 << 9)) != 0;
builder.settings_["allowTrailingCommas"] = (hash_settings & (1 << 10)) != 0;
// Bit 11 selects a small stack limit taken from the upper 16 bits, so that
// the depth-limit error paths are reachable without huge inputs.
if (hash_settings & (1 << 11)) {
builder.settings_["stackLimit"] = (hash_settings >> 16) & 0x3FF;
}

// An unknown key is silently ignored by newCharReader(), so a typo here
// would quietly stop the fuzzer from exercising that setting.
if (!builder.validate(nullptr)) {
std::abort();
}

std::unique_ptr<Json::CharReader> reader(builder.newCharReader());

Expand Down
27 changes: 22 additions & 5 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4324,11 +4324,28 @@ 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_LOCAL(FuzzTest, fuzzDoesntCrash) {
const std::string example = "{}";
JSONTEST_ASSERT_EQUAL(
0,
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(example.c_str()),
example.size()));
// The first four bytes select the reader settings (little-endian).
const auto withSettings = [](uint32_t settings, const std::string& doc) {
std::string input;
for (int i = 0; i < 4; ++i) {
input += static_cast<char>((settings >> (8 * i)) & 0xFF);
}
return input + doc;
};
const std::string examples[] = {
"{}",
withSettings(0, "{\"a\":[1,2]}"),
// Every boolean setting on, with a stack limit of 16.
withSettings(0x0010FFFF, "{\"a\":[1,2,],} // comment"),
// A stack limit of 1, so the nested arrays exceed it.
withSettings(0x00010800, "[[[[1]]]]"),
};
for (const auto& example : examples) {
JSONTEST_ASSERT_EQUAL(
0,
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(example.data()),
example.size()));
}
}

struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
Expand Down
Loading