Factor out more repeated error paths and test boilerplate

Library:
- add throw_cannot_use_with() for the 34 copies of type_error.304-312
  "cannot use X with Y"
- iter_impl: add throw_cannot_get_value() (invalid_iterator.214)
- parser: add syntax_error() for the 12 parse_error.101 sites
- ordered_map: share the four at() bodies via at_impl()
- json_sax_dom_callback_parser: add pop_container() for end_object()
  and end_array()
- binary_reader: build the two UBJSON/BJData length-type messages with
  concat() and last_byte_error()

Tests:
- move same_value(), the NDEBUG guard, and step 0 (parse without
  exceptions) of the seven fuzzer drivers into tests/src/fuzzer_common.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-07 17:29:58 +02:00
1 parent 546350440f
commit c3068e9f95
16 files changed
+337 -481

No files matched your search

+4 -29
View File
@@ -53,20 +53,9 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// value-stable comparison for the round-trip checks below; see the note
// above on why this compares dump()s rather than the json values directly
@@ -80,23 +69,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_bjdata(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_bjdata(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -28,20 +28,9 @@ drivers.
#include <cassert>
#include <sstream>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
namespace
{
@@ -72,23 +61,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_bon8(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_bon8(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -23,43 +23,18 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_bson(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_bson(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -23,43 +23,18 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_cbor(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_cbor(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -24,41 +24,16 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::parse(data, data + size, nullptr, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::parse(data, data + size, nullptr, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -23,43 +23,18 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_msgpack(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_msgpack(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+4 -29
View File
@@ -32,43 +32,18 @@ drivers.
#include <cassert>
#include <nlohmann/json.hpp>
#include "fuzzer_common.hpp"
// the round-trip checks below are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
using json = nlohmann::json;
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
static bool same_value(const json& lhs, const json& rhs)
{
return lhs.dump(-1, ' ', false, json::error_handler_t::keep) == rhs.dump(-1, ' ', false, json::error_handler_t::keep);
}
using nlohmann::json;
// see http://llvm.org/docs/LibFuzzer.html
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::vector<uint8_t> const vec1(data, data + size);
// step 0: parse input without exceptions; a parse error must then be
// reported as a discarded value, never thrown
json j_noexcept;
// step 0: parse input without exceptions
bool noexcept_threw = false;
try
{
j_noexcept = json::from_ubjson(vec1, true, false);
}
catch (const json::parse_error&)
{
assert(false);
}
catch (const json::exception&)
{
// type and out-of-range errors are not parse errors and still throw
noexcept_threw = true;
}
json const j_noexcept = parse_without_exceptions([&] { return json::from_ubjson(vec1, true, false); }, noexcept_threw);
// whether step 1 succeeded; if not, the catch blocks below check that
// step 0 failed, too
bool parsed = false;
+49
View File
@@ -0,0 +1,49 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// code shared by the fuzzer drivers tests/src/fuzzer-parse_*.cpp
#pragma once
#include <cassert> // assert
#include <nlohmann/json.hpp>
// the round-trip checks of the drivers are assertions; NDEBUG would compile them away
#ifdef NDEBUG
#error "the fuzzer drivers must be built without NDEBUG"
#endif
// compares dumps rather than values, because NaN != NaN; keep writes strings
// byte for byte, so ill-formed UTF-8 that a binary reader accepts cannot throw
inline bool same_value(const nlohmann::json& lhs, const nlohmann::json& rhs)
{
return lhs.dump(-1, ' ', false, nlohmann::json::error_handler_t::keep) == rhs.dump(-1, ' ', false, nlohmann::json::error_handler_t::keep);
}
// step 0 of each driver: parse the input without exceptions; a parse error
// must then be reported as a discarded value, never thrown. Type and
// out-of-range errors are not parse errors and still throw; then @a threw is
// set and null is returned.
template<typename Parse>
nlohmann::json parse_without_exceptions(Parse parse, bool& threw)
{
threw = false;
try
{
return parse();
}
catch (const nlohmann::json::parse_error&)
{
assert(false);
}
catch (const nlohmann::json::exception&)
{
threw = true;
}
return {};
}