From efcf9efb4f804fedb0a66f5218c850198d616637 Mon Sep 17 00:00:00 2001 From: Nikhil <77785504+nikhilreddydev@users.noreply.github.com> Date: Sun, 3 Aug 2025 00:36:01 +0530 Subject: [PATCH] Fixes #4854 Explicitly handle nullptr in sax_parse (#4873) * handle nullptr explicitly Signed-off-by: Nikhil * add test Signed-off-by: Nikhil * make amalgamate Signed-off-by: Nikhil * Fix formatting Signed-off-by: Nikhil * move sax parse test to relevant unit test file Signed-off-by: Nikhil * extend exceptions.md to include other_error.502 Signed-off-by: Nikhil * Better exception messages Signed-off-by: Nikhil * link sax_parse function Signed-off-by: Nikhil * fix string Signed-off-by: Nikhil * amalgamate Signed-off-by: Nikhil * fix clang-tidy checks Signed-off-by: Nikhil * cover valid handler with no throw Signed-off-by: Nikhil * Add tests for other two overloads Signed-off-by: Nikhil * cover overload with valid sax handler Signed-off-by: Nikhil * pass an rvalue Signed-off-by: Nikhil * ignore -Wtautological-pointer-compare Signed-off-by: Nikhil * ignore clang-analyzer-core.NonNullParamChecker Signed-off-by: Nikhil * ignore gcc -Wnonnull-compare Signed-off-by: Nikhil * ignore undefined-behaviour-sanitizer Signed-off-by: Nikhil * nest directives to ignore sanitizer errors Signed-off-by: Nikhil * use elif Signed-off-by: Nikhil --------- Signed-off-by: Nikhil --- docs/mkdocs/docs/home/exceptions.md | 10 ++++++ include/nlohmann/json.hpp | 48 +++++++++++++++++++++++++++++ single_include/nlohmann/json.hpp | 48 +++++++++++++++++++++++++++++ tests/src/unit-class_parser.cpp | 27 ++++++++++++++++ 4 files changed, 133 insertions(+) diff --git a/docs/mkdocs/docs/home/exceptions.md b/docs/mkdocs/docs/home/exceptions.md index cfa4f57a2..4b6eb88b3 100644 --- a/docs/mkdocs/docs/home/exceptions.md +++ b/docs/mkdocs/docs/home/exceptions.md @@ -900,3 +900,13 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed ``` [json.exception.other_error.501] unsuccessful: {"op":"test","path":"/baz","value":"bar"} ``` + +### json.exception.other_error.502 + +This exception occurs when a null pointer is passed as SAX handler in [sax_parse](../api/basic_json/sax_parse.md) function. + +!!! failure "Example message" + + ``` + [json.exception.other_error.502] SAX handler must not be null + ``` diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 55ec39d24..a49a28b8a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4122,6 +4122,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) @@ -4138,6 +4154,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) @@ -4158,6 +4190,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = i.get(); return format == input_format_t::json // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1fd570576..e66265542 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -24225,6 +24225,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) @@ -24241,6 +24257,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json ? parser(std::move(ia), nullptr, true, ignore_comments, ignore_trailing_commas).sax_parse(sax, strict) @@ -24261,6 +24293,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool ignore_comments = false, const bool ignore_trailing_commas = false) { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (sax == nullptr) + { + JSON_THROW(other_error::create(502, "SAX handler must not be null", nullptr)); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif auto ia = i.get(); return format == input_format_t::json // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index 394bad432..58d27aca8 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -1644,6 +1644,33 @@ TEST_CASE("parser class") SECTION("SAX parser") { + SECTION("null sax handler") + { +# if defined(__has_feature) +#if !__has_feature(undefined_behavior_sanitizer) + const std::string s = "some_string"; + SaxCountdown* p = nullptr; + CHECK_THROWS_WITH_AS(json::sax_parse(s, p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(s.begin(), s.end(), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(nlohmann::detail::span_input_adapter(s.c_str(), s.size()), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) +#endif +#else + const std::string s = "some_string"; + SaxCountdown* p = nullptr; + CHECK_THROWS_WITH_AS(json::sax_parse(s, p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(s.begin(), s.end(), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) + CHECK_THROWS_WITH_AS(json::sax_parse(nlohmann::detail::span_input_adapter(s.c_str(), s.size()), p), "[json.exception.other_error.502] SAX handler must not be null", json::other_error&); // NOLINT(clang-analyzer-core.NonNullParamChecker) +#endif + } + + SECTION("valid sax handler") + { + const std::string str = "some_string"; + SaxCountdown s(1); + CHECK(json::sax_parse(str, &s) == false); + CHECK(json::sax_parse(nlohmann::detail::span_input_adapter(str.c_str(), str.size()), &s) == false); + } + SECTION("} without value") { SaxCountdown s(1);