opa icon indicating copy to clipboard operation
opa copied to clipboard

Stack overflow in type checker given recursive ref in schema

Open anderseknert opened this issue 2 years ago • 3 comments

This came up when trying to make Regal commands make use of a schema I wrote for the AST. A few elements of that schema are recursive by necessity, like else clauses in rules, which are essentially references to other "rules", which may contain other else clauses, and so on. Reducing it to a minimal example:

schema.json

{
    "$ref": "#/$defs/foo",
    "$defs": {
        "foo": {
            "type": "object",
            "properties": {
                "foo": {
                    "$ref": "#/$defs/foo"
                }
            }
        }
    }
}

policy.rego

package p

allow := input.foo
❯ opa eval -d policy.rego -s schema.json data.p
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0x14020700380 stack=[0x14020700000, 0x14040700000]
fatal error: stack overflow

runtime stack:
... long stack trace here ...

This prevents type checking for these type of attributes. As a temporary workaround, I can create something like "else1", "else2", "else3" types, but... whatever number I come up with, I'm sure some AST will have one more 😄

It's worth noting that I tried the same schema using the json.match_schema, but luckily it did not have this problem.

anderseknert avatar Jul 16 '23 21:07 anderseknert

Spotted in the wild https://github.com/infracost/infracost/blob/032cba294b9c34184eb7616697f0f87d0378e5b8/cmd/jsonschema/main.go#L72

anderseknert avatar Nov 15 '23 10:11 anderseknert

We've experienced this one as well (or our users have reported). See this for details.

Any suggestions how we can get around it? Or if something is in the works already?

simar7 avatar Jan 10 '24 03:01 simar7

Hi @simar7 👋 Nothing in the works. If someone has the bandwidth to tackle this, that'd be great.

As for a workaround, that would be to remove the recursive reference in the schema by setting the type to a generic object, or whatever the type is valid. We do this in Regal in some places, like here, where the else clause is recursive (as it may contain other else clauses, which may contain other else clauses...).

Obviously, that means this element won't be fully covered by the schema checks, but that's likely preferable over the stack overflow error.

anderseknert avatar Jan 10 '24 07:01 anderseknert