Skip to content

Fix findInArray misuse on MappingNodes in equality and contains#2645

Merged
mikefarah merged 1 commit intomikefarah:masterfrom
jandubois:fix-map-equality-null-key
Apr 6, 2026
Merged

Fix findInArray misuse on MappingNodes in equality and contains#2645
mikefarah merged 1 commit intomikefarah:masterfrom
jandubois:fix-map-equality-null-key

Conversation

@jandubois
Copy link
Copy Markdown
Contributor

recurseNodeObjectEqual and containsObject both used findInArray to locate keys in a MappingNode's Content array. findInArray steps by 1, so it matches against both keys (even indices) and values (odd indices).

In recurseNodeObjectEqual, when a null key in the LHS matched a null value in the RHS at the last position, rhs.Content[indexInRHS+1] accessed an out-of-bounds index, causing a panic.

In containsObject, a %2 guard prevented the panic but introduced false negatives: when a null value appeared before the actual null key, findInArray returned the value's odd index, the guard rejected it, and the function reported the key as missing.

Both functions now use findKeyInMap, which steps by 2 and compares only key positions. The %2 guard in containsObject is removed.

Reproducer for the panic (recurseNodeObjectEqual):

echo '? [{~: ~}]
: v1
? [{2: ~}]
: v2' | yq '. += .'

Reproducer for the false negative (containsObject):

printf '? 1\n: ~\n? ~\n: x\n' | yq 'contains({~: "x"})'

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

recurseNodeObjectEqual and containsObject both used findInArray to
locate keys in a MappingNode's Content array. findInArray steps by 1,
so it matches against both keys (even indices) and values (odd indices).

In recurseNodeObjectEqual, when a null key in the LHS matched a null
value in the RHS at the last position, rhs.Content[indexInRHS+1]
accessed an out-of-bounds index, causing a panic.

In containsObject, a %2 guard prevented the panic but introduced false
negatives: when a null value appeared before the actual null key,
findInArray returned the value's odd index, the guard rejected it, and
the function reported the key as missing.

Both functions now use findKeyInMap, which steps by 2 and compares only
key positions. The %2 guard in containsObject is removed.

Reproducer for the panic (recurseNodeObjectEqual):

    echo '? [{~: ~}]
    : v1
    ? [{2: ~}]
    : v2' | yq '. += .'

Reproducer for the false negative (containsObject):

    printf '? 1\n: ~\n? ~\n: x\n' | yq 'contains({~: "x"})'

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

Signed-off-by: Jan Dubois <jan@jandubois.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
value := lhs.Content[index+1]

indexInRHS := findInArray(rhs, key)
indexInRHS := findKeyInMap(rhs, key)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't beleive I was doing this tbh - nice find!

@mikefarah mikefarah merged commit b0ba958 into mikefarah:master Apr 6, 2026
3 checks passed
@jandubois jandubois deleted the fix-map-equality-null-key branch April 6, 2026 17:01
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.

2 participants