From 740f97adab64dfcb60bc4909d69e8d8ac21bd1d8 Mon Sep 17 00:00:00 2001 From: Angadi Yashaswini Date: Tue, 14 Jul 2026 20:29:14 +0530 Subject: [PATCH 1/2] Fix out-of-bounds read in dump() for large indentation widths Signed-off-by: Angadi Yashaswini --- include/nlohmann/detail/output/serializer.hpp | 6 ++-- single_include/nlohmann/json.hpp | 6 ++-- tests/src/unit-serialization.cpp | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 0b608f8e2d..e9d26e3895 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -126,7 +126,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -199,7 +199,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -260,7 +260,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 03d2351736..18d8ca566f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19833,7 +19833,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -19906,7 +19906,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -19967,7 +19967,7 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } diff --git a/tests/src/unit-serialization.cpp b/tests/src/unit-serialization.cpp index f55ed84704..44ee0a5bc2 100644 --- a/tests/src/unit-serialization.cpp +++ b/tests/src/unit-serialization.cpp @@ -150,6 +150,36 @@ TEST_CASE("serialization") } } + SECTION("dump with large indentation") + { + // the indentation buffer starts at 512 characters and grows on demand; + // a width that needs more room than a single doubling provides must keep + // growing the buffer rather than emit past its end + const unsigned int width = 1100; + + SECTION("object") + { + const json j = {{"outer", {{"inner", 1}}}}; + const std::string s = j.dump(static_cast(width)); + CHECK(s.find('\n' + std::string(width, ' ') + "\"outer\"") != std::string::npos); + CHECK(s.find('\n' + std::string(2 * width, ' ') + "\"inner\"") != std::string::npos); + } + + SECTION("array") + { + const json j = json::array({json::array({1})}); + const std::string s = j.dump(static_cast(width)); + CHECK(s.find('\n' + std::string(2 * width, ' ') + '1') != std::string::npos); + } + + SECTION("binary") + { + const json j = json::binary({1, 2, 3}); + const std::string s = j.dump(static_cast(width)); + CHECK(s.find('\n' + std::string(width, ' ') + "\"bytes\"") != std::string::npos); + } + } + SECTION("to_string") { auto test = [&](std::string const & input, std::string const & expected) From c58cf4b8ef5844baf00a24f5e899567844f2cec3 Mon Sep 17 00:00:00 2001 From: Angadi Yashaswini Date: Wed, 15 Jul 2026 15:51:13 +0530 Subject: [PATCH 2/2] Resize indent_string to the needed width in a single step Signed-off-by: Angadi Yashaswini --- include/nlohmann/detail/output/serializer.hpp | 12 ++++++------ single_include/nlohmann/json.hpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index e9d26e3895..3d546c2706 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -126,9 +126,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } // first n-1 elements @@ -199,9 +199,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } // first n-1 elements @@ -260,9 +260,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } o->write_characters(indent_string.c_str(), new_indent); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 18d8ca566f..5d66ee375d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19833,9 +19833,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } // first n-1 elements @@ -19906,9 +19906,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } // first n-1 elements @@ -19967,9 +19967,9 @@ class serializer // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - while (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - indent_string.resize(indent_string.size() * 2, ' '); + indent_string.resize((std::max)(indent_string.size() * 2, static_cast(new_indent)), ' '); } o->write_characters(indent_string.c_str(), new_indent);