stack overflow
Description
I would like to report one stack overflow bug I encountered while testing the latest version(v 3.12.0) of json. It appears that json may crash when given malformed input.
The gdb debugging log is in json/gdb_debug_log, and the input is in json/input
Reproduction steps
- gdb json
- run input/stack_overflow
Expected vs. actual results
expected: program runs normally
actual results: Program received signal SIGSEGV, Segmentation fault.
0x000000000047b85b in nlohmann::json_abi_v3_12_0::detail::binary_reader<nlohmann::json_abi_v3_12_0::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits
Minimal code example
Error messages
Compiler and operating system
g++ 9.4.0 ubuntu22.04.1
Library version
3.12.0
Validation
- [x] The bug also occurs if the latest version from the
developbranch is used. - [x] I can successfully compile and run the unit tests.
This is a known issue of the library. The input creates 5538 nested arrays which are parsed with 5538 recursive calls. That issue exists for all input formats - both binary and JSON.
Unless somebody wants to look into this issue and makes the parser non-recursive, I would close the issue as "won't fix".
Hi, Can I look into this?
Sure.
Hi @nlohmann,
I'm working on a coroutine-based approach (C++20) to eliminate stack overflow in the binary format parsers (UBJSON/BSON/CBOR) when handling deeply nested data.
The implementation would use feature flags to preserve the current implementation for older standards while providing stack-safe parsing for C++20+.
Provided I guard it for older C++ standards, would this be something you'd consider for the library?
Probably not. What do you have in mind?
Here is a prototype implementation in my fork: https://github.com/AhmedMagdy9876/nlohmann_json/commit/ce5d490e63a42699c7faf654ba939a3ddc13d130
The idea is to remove the unbounded call-stack recursion in the binary parsers and the serializer by replacing the strict recursive descent with small coroutine steps. Each parsing step becomes a coroutine that co_awaits the next one, so a suspension chain replaces the recursion chain without changing the control flow logic. This keeps the existing structure but moves the recursion off the C++ call stack and into the coroutine frame.
The coroutine implementation only activates when the compiler supports C++20 coroutines; otherwise the original recursive codepath is used untouched, and the stack overflow issue will still exist. This commit is just intended to explain the approach. I’m happy to simplify or adjust the design if you think the direction is viable.