Skip to content

Fix unbounded recursion in expression evaluation via circular reference detection#851

Merged
Haishi2016 merged 2 commits into
eclipse-symphony:mainfrom
lirenjie95:fix/expression-circular-reference
Jul 23, 2026
Merged

Fix unbounded recursion in expression evaluation via circular reference detection#851
Haishi2016 merged 2 commits into
eclipse-symphony:mainfrom
lirenjie95:fix/expression-circular-reference

Conversation

@lirenjie95

Copy link
Copy Markdown
Contributor

Fixes #850

Root cause

When the stage manager evaluates an activation's inputs, any stored value that looks like an expression is evaluated again (readProperty / readPropertyInterface in api/pkg/apis/v1alpha1/utils/parser.go): a value wrapped in ${{...}} is fed back into NewParser(v).Eval(evalCtx) with no cycle detection. A self-referencing input such as a: "${{$input(a)}}" (or mutual references aba) recurses until the process runs out of stack — a fatal, unrecoverable error in Go that takes symphony-api down.

Fix

Apply the same circular dependency guard that #433 introduced for $config to the other expression sources. Every recursion cycle necessarily passes through one of the $property / $input / $output / $trigger function branches, so the check is placed at those four call sites:

  • before re-reading a stored value, HasCircularDependency(object, key, context) rejects a key that is already on the current resolution path, with the same error shape as the catalog provider (BadConfig, "Detect circular dependency, object: %s, field: %s")
  • otherwise the key is recorded on the path via the existing UpdateDependencyList + DeepCopyDependencyList helpers and carried down through the by-value EvaluationContext, so sibling branches never see each other's path

No changes to the coa module — the machinery from #433 is reused as-is.

Tests

  • parser_test.go: self-referencing input returns a BadConfig error instead of recursing; mutually referencing inputs likewise; legitimate nested references without cycles still evaluate; a pre-seeded resolution path is rejected on first access
  • stage-manager_test.go: an activation with a self-referencing input fails the stage with the circular dependency error while the manager survives and processes subsequent activations normally

@lirenjie95
lirenjie95 requested a review from Haishi2016 as a code owner July 23, 2026 03:54
Activation inputs are exposed as triggers and are not evaluated unless a
stage input references them, so the self-referencing value must live in
the stage spec's inputs to exercise the circular dependency check.
@Haishi2016
Haishi2016 merged commit 7984367 into eclipse-symphony:main Jul 23, 2026
16 checks passed
@lirenjie95
lirenjie95 deleted the fix/expression-circular-reference branch July 24, 2026 03:26
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.

Self-referencing activation input expressions crash symphony-api via unbounded recursion

2 participants