Skip to content

Fix stack overflow from circular alias in traverse#2647

Merged
mikefarah merged 1 commit intomikefarah:masterfrom
jandubois:fix-alias-cycle
Apr 6, 2026
Merged

Fix stack overflow from circular alias in traverse#2647
mikefarah merged 1 commit intomikefarah:masterfrom
jandubois:fix-alias-cycle

Conversation

@jandubois
Copy link
Copy Markdown
Contributor

go-yaml accepts cross-document alias references, which the YAML spec forbids (anchors are scoped to a single document). When a nested assignment targets such an alias, UpdateFrom copies the Alias field between nodes, creating a self-referencing AliasNode. Both traverse() and traverseArrayIndices() then follow this cycle indefinitely.

Extract resolveAliasChain(), which follows aliases iteratively with a visited set and returns an error on cycles. Both traverse() and traverseArrayIndices() now call it, eliminating the recursive alias handling in both code paths.

Note: traverseMergeAnchor() also dereferences aliases (lines 358 and 371) but with single-step assignment, not recursion. A self-referencing alias there falls through the kind switch silently rather than crashing. Using resolveAliasChain() in that function would produce a clear error instead of silently dropping the node.

Reproducer (stack overflow before this fix, returns error after):

echo '&-- a
---
*--' | yq eval-all '. = (.x = 1)'

Found by OSS-Fuzz via the lima project's FuzzEvaluateExpression target. https://issues.oss-fuzz.com/issues/390467412

go-yaml accepts cross-document alias references, which the YAML spec
forbids (anchors are scoped to a single document). When a nested
assignment targets such an alias, UpdateFrom copies the Alias field
between nodes, creating a self-referencing AliasNode. Both traverse()
and traverseArrayIndices() then follow this cycle indefinitely.

Extract resolveAliasChain(), which follows aliases iteratively with a
visited set and returns an error on cycles. Both traverse() and
traverseArrayIndices() now call it, eliminating the recursive alias
handling in both code paths.

Note: traverseMergeAnchor() also dereferences aliases (lines 358 and
371) but with single-step assignment, not recursion. A self-referencing
alias there falls through the kind switch silently rather than
crashing. Using resolveAliasChain() in that function would produce a
clear error instead of silently dropping the node.

Reproducer (stack overflow before this fix, returns error after):

    echo '&-- a
    ---
    *--' | yq eval-all '. = (.x = 1)'

Found by OSS-Fuzz via the lima project's FuzzEvaluateExpression target.
https://issues.oss-fuzz.com/issues/390467412

Signed-off-by: Jan Dubois <jan@jandubois.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ccoVeille
Copy link
Copy Markdown
Contributor

go-yaml accepts cross-document alias references, which the YAML spec forbids (anchors are scoped to a single document).

Hey @ingydotnet

What are your thoughts on this statement?

We should detect at go-yaml level if it's relevant

@mikefarah mikefarah merged commit 0374ad6 into mikefarah:master Apr 6, 2026
3 checks passed
@jandubois jandubois deleted the fix-alias-cycle branch April 6, 2026 17:00
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.

3 participants