Skip to content

feat: introduce maximum parser recursion depth #1230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

thevilledev
Copy link
Contributor

Problem

Parsing extremely deeply nested structures (e.g., nested arrays [[[...]]], objects {a:{a:{...}}}, or long chains of unary/binary operators) could lead to excessive recursion in the C++ parser functions, potentially exhausting the native call stack and crashing the interpreter.

Solution

A constant MAX_PARSER_DEPTH (set to 1000) is introduced in core/parser.cpp. Recursive parsing functions (parse, parseArgs, parseParams, parseBind, parseObjectRemainder, parseComprehensionSpecs, parseTerminalBracketsOrUnary, maybeParseGreedy, parseInfix) now track the current recursion depth and throw a StaticError if this limit is exceeded. This ensures the parser terminates gracefully for pathological inputs instead of crashing.

The current_depth parameter is generally incremented by 1 whenever a parsing function makes a recursive call to parse a distinct sub-expression or nested structure (e.g., calling parse from within parseObjectRemainder to handle a field's value, or calling parse from parseInfix to handle the right-hand operand). This accurately tracks the syntactic nesting depth. Calls that are part of processing the same structural level (like parseParams calling parseArgs internally) might pass the depth unchanged.

A new test case (test_suite/error.parse.deep_array_nesting.jsonnet) has been added to verify that the depth limit is correctly enforced and produces the expected static error. This test case uses deeply nested arrays, which demonstrably causes a segmentation fault on the master branch due to native stack overflow during parsing, but is now caught gracefully by the introduced limit.

I also updated all function doc strings as they were either missing or outdated.

Why a constant?

Making this limit a constant (rather than a command-line configurable option like --max-stack) was a deliberate design choice at this point:

  • MAX_PARSER_DEPTH guards the parsing stage against C++ stack overflows, while --max-stack limits the evaluation stage's Jsonnet VM call stack.
  • Exceeding a parse depth of 1000 is highly unlikely for typical, human-written Jsonnet code. It often indicates a structural issue in the code itself rather than a need for a higher limit.
  • Adding a configuration option would increase CLI complexity and require plumbing the value through the library.

That being said, this is open for discussion and feedback.

NOTE: originally reported through Google OSS VRP and public disclosure was preferred.

Add a recursion depth limit to prevent stack overflow vulnerabilities
when parsing deeply nested Jsonnet expressions. This addresses potential
denial-of-service attacks where malicious inputs with excessive nesting
could crash the parser.

Key changes:
- Introduce MAX_PARSER_DEPTH constant (1000) to limit parser recursion
- Add depth parameter to all parsing functions
- Check depth limit before parsing recursively nested structures
- Throw clear error message when maximum depth is exceeded
- Improve documentation for parsing functions
- Add test case to verify limit enforcement

Signed-off-by: Ville Vesilehto <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant