Repository navigation
Stack overflow in binary format writers (CBOR/MessagePack/UBJSON/BSON) due to unbounded recursion #5392
Description
Summary
The binary format writers (binary_writer.hpp) recurse once per nesting level with no depth limit, so serializing a deeply nested value crashes the process. This is the counterpart of #5104, which covers the binary readers; both directions are affected, and neither is fixed.
The text parser is iterative, so json::parse accepts input of any depth. A value that the library happily builds can therefore crash it on the way back out.
Affected functions
json::to_cbor()json::to_msgpack()json::to_ubjson()(and BJData)json::to_bson()
Reproduction
Against develop (734fd30), on macOS/arm64 with the default 8 MB stack:
#include <nlohmann/json.hpp>
#include <string>
int main()
{
const std::size_t depth = 200000;
const nlohmann::json j = nlohmann::json::parse(
std::string(depth, '[') + "0" + std::string(depth, ']'));
const auto v = nlohmann::json::to_cbor(j); // SIGSEGV
return static_cast<int>(v.size());
}| function | -O0 |
-O2 |
|---|---|---|
to_cbor |
crashes at depth 100,000 | crashes at depth 200,000 |
to_msgpack |
crashes at depth 100,000 | crashes at depth 200,000 |
to_ubjson |
crashes at depth 100,000 | crashes at depth 100,000 |
to_bson |
crashes at depth 100,000 | crashes at depth 100,000 |
The depth needed varies with build and stack size, as usual for stack exhaustion; CBOR and MessagePack survive depth 100,000 at -O2 on this machine and fail at 200,000. BSON is measured with a nested object, since BSON requires an object at the root.
Root cause
Each writer calls itself for every element:
- CBOR:
write_cbor()(binary_writer.hpp:96) → binary_writer.hpp:303 (array), :405/:406 (object) - MessagePack:
write_msgpack()(:420) → :614 (array), :727/:728 (object) - UBJSON/BJData:
write_ubjson()(:747) → :842 (array), :944 (object) - BSON:
write_bson_element()(:1238) →write_bson_object()(:1302) /write_bson_array()(:1160) →write_bson_element()(:1306/:1166)
BSON recurses twice over the same value. Besides writing, it computes each document's length up front through a second, independent recursive walk: calc_bson_element_size() (:1190) → calc_bson_object_size() (:1285) / calc_bson_array_size() (:1133) → calc_bson_element_size() (:1290/:1139). Both walks need bounding, which makes BSON the most involved of the four.
Suggested fix
The same shape used for the destructor in #1436 and for copying, serializing and comparing in #5389 / #5285 / #5390: descend a bounded number of levels, then finish the rest on an explicit stack. That keeps the fast path for ordinary values and removes the limit on depth, without a new exception or a new macro.
CBOR, MessagePack and UBJSON are structurally similar and could share an approach. BSON's size pass is separate work and may be worth its own change.
Related
- Stack overflow in binary format parsers (CBOR/MessagePack/UBJSON/BSON) due to unbounded recursion #5104 — the same problem in the binary readers
- Stack overflow in copy constructor and dump() on deeply nested json (destructor was fixed in #1436) #5387 — the same problem in the copy constructor and
dump() - Prevent stackoverflow caused by recursive deconstruction #1436 — where the destructor was fixed this way
Written by Claude Code.
Activity
ayush-singh-0601 commentedon Sep 10, 2026
Opened #5518 for the CBOR/MessagePack/UBJSON side of this. BSON still needs the size pass, so I left that out.
Stop binary writers overflowing the stack on deep values
Stop binary writers overflowing the stack on deep values
Write BSON in linear time, without recursing per nesting level
Write BSON in linear time, without recursing per nesting level
Write BSON in linear time, without recursing per nesting level
Write BSON in linear time, without recursing per nesting level
nlohmann commentedon Sep 25, 2026
Reopening: #5553 fixed only the BSON writer (its commit message said "Fixes #5392 for BSON", which GitHub read as closing the whole issue). The CBOR, MessagePack, and UBJSON/BJData writers in
binary_writer.hppstill recurse once per nesting level ondevelop; that part is tracked in #5518. This issue closes when that lands.Written by Claude Code.
Write BSON in linear time, without recursing per nesting level (nlohm…
Stop binary writers overflowing the stack on deep values
Stop binary writers overflowing the stack on deep values (#5781)