Skip to content

Fix indentation overflow and correctness bug#5186

Open
nlohmann wants to merge 3 commits into
developfrom
fix-indent_string-resize
Open

Fix indentation overflow and correctness bug#5186
nlohmann wants to merge 3 commits into
developfrom
fix-indent_string-resize

Conversation

@nlohmann

Copy link
Copy Markdown
Owner

There are two issues in the current dump function:

  • Overflow (reported by @manop55555): Under large indent_step values, indent_string growth is insufficient and can lead to buffer over-read / memory safety problems depending on write_characters() behavior. A GitHub Security Advisory will be published referencing this PR.
  • In the resize of indent_string, a space character instead of the passed indent_char was used.

nlohmann added 2 commits May 20, 2026 21:56
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label May 20, 2026
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

@LD-RW LD-RW left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Took a look at the indentation overflow fix — the guarded resize plus honoring indent_char both look right. Left two notes inline: a suggestion to extract the now-triplicated resize-and-assert guard into a small reserve_indent helper so the three branches can't drift, and a question about whether the unsigned current_indent + indent_step accumulation can still wrap on deeply nested input even after this fix. Neither is a blocker — the second is more "is this in scope?" than a defect claim.

@@ -128,7 +128,8 @@ class serializer
const auto new_indent = current_indent + indent_step;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: this fixes the buffer-growth side (the * 2 could undershoot a large indent_step), but is the accumulation of new_indent itself still vulnerable to wraparound? new_indent = current_indent + indent_step is unsigned, and the public dump(const int indent, ...) lets a caller pass a very large indent. For a deeply nested document the running current_indent + indent_step can overflow unsigned int and wrap to a small value; the indent_string.size() < new_indent guard would then pass on the wrapped-small new_indent, and write_characters(indent_string.c_str(), new_indent) would emit silently-truncated indentation rather than over-reading. Is that case considered out of scope here, or worth a saturating check / assert on new_indent as well?

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<std::size_t>(new_indent)), indent_char);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: this resize-and-assert guard is now duplicated verbatim at all three sites (object branch L131, array branch L205, single-element branch L267). The original bug existed in three places precisely because the logic was copy-pasted, and the fix had to be pasted three times too — so the next change to the growth policy has three spots to keep in sync. Extracting a small private helper would collapse them into one and make future divergence impossible:

void reserve_indent(std::size_t new_indent)
{
    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
    {
        indent_string.resize((std::max)(indent_string.size() * 2, new_indent), indent_char);
    }
    JSON_ASSERT(indent_string.size() >= new_indent);
}

then each site becomes a single reserve_indent(new_indent); call before write_characters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

M review needed It would be great if someone could review the proposed changes. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants