Skip to content

fix: add stack depth accounting to rawEquals - #892

Open
prasanna8585 wants to merge 1 commit into
google:masterfrom
prasanna8585:fix/rawequals-stack-depth-accounting
Open

fix: add stack depth accounting to rawEquals#892
prasanna8585 wants to merge 1 commit into
google:masterfrom
prasanna8585:fix/rawequals-stack-depth-accounting

Conversation

@prasanna8585

@prasanna8585 prasanna8585 commented Jul 23, 2026

Copy link
Copy Markdown

Problem

rawEquals in builtins.go, which implements jsonnet's == and != operators, recurses natively via the Go call stack when comparing nested arrays or objects. It has no stack-depth accounting anywhere in the call chain.

A self-referential jsonnet value compared with ==:

{a: $} == {a: $}

causes rawEquals to recurse into itself indefinitely, consuming memory until the process is killed by the OS (OOM) or the Go runtime's stack limit is hit.

Precedent

This is the same bug class already fixed in builtinManifestJSONEx, builtinManifestYamlDoc, and builtinManifestTomlEx (depth-counter pattern seeded from i.stack.limit, decremented per recursive call, erroring with "max manifest depth exceeded"). rawEquals was never covered by that sweep and remains vulnerable to the identical trigger pattern.

Fix

Adds a depth int parameter to rawEquals, seeded from i.stack.limit (the interpreter's own configured stack limit, default 500) at all 5 entry points, and decremented on each of the 2 recursive call sites (array element comparison, object field comparison). Errors with "max equality depth exceeded, possible infinite recursion" when exhausted — matching the existing manifest-builtin error message style exactly.

Testing

Verified with {a: $} == {a: $} — with the fix, this now returns a clean error instead of hanging indefinitely with unbounded memory growth.

rawEquals (which implements jsonnet's == and != operators) recursed
natively via the Go call stack when comparing nested arrays or objects,
with no stack-depth accounting anywhere in the call chain.

A self-referential jsonnet value compared with == (e.g. {a: $} == {a: $})
causes rawEquals to recurse indefinitely, consuming memory until OOM-kill
or hitting the Go runtime's stack limit.

This is the same bug class already fixed in manifestJSON/manifestYaml/
manifestToml builtins (depth-counter pattern seeded from i.stack.limit),
but rawEquals was not covered by that sweep.

Fix: add a depth int parameter to rawEquals, seeded from i.stack.limit
at each of its 5 entry points, decremented on each of the 2 recursive
call sites (array element comparison, object field comparison), erroring
with 'max equality depth exceeded' when exhausted -- consistent with the
existing manifest-builtin error message pattern.
@prasanna8585

Copy link
Copy Markdown
Author

Hi @johnbartholomew @rohitjangid,

Just checking in to see if you've had a chance to look at the patch strategy for this. I'm happy to spin up the code changes for the fix in a private fork if that helps speed things up.

Thanks again for your time!

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