Skip to content

Stack Overflow in Recursive Object Merge

Moderate
itchyny published GHSA-mg96-6h3q-g846 May 5, 2026

Package

jqlang/jq

Affected versions

<= 1.8.1

Patched versions

1.8.2

Description

Summary

Unbounded recursion in jv_object_merge_recursive() allows a crafted jq program to crash the process with a segfault. The function is reachable through the * operator when both operands are objects. This is a denial of service on jq 1.8.1 and below.

Details

The jv_object_merge_recursive() function in src/jv.c recursively merges nested objects without any depth limit. When two deeply nested objects share the same key structure, the function descends into each matching key pair and calls itself again. There is no depth guard on this recursion. If the objects are nested deeply enough, the C call stack is exhausted and the process crashes with SIGSEGV.

This function is exposed to users through the * operator. When both operands of * are objects, builtin.c dispatches to jv_object_merge_recursive():

} else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) {
  return jv_object_merge_recursive(a, b);
}

The recursive merge code in src/jv.c:

jv jv_object_merge_recursive(jv a, jv b) {
  assert(JVP_HAS_KIND(a, JV_KIND_OBJECT));
  assert(JVP_HAS_KIND(b, JV_KIND_OBJECT));

  jv_object_foreach(b, k, v) {
    jv elem = jv_object_get(jv_copy(a), jv_copy(k));
    if (jv_is_valid(elem) &&
        JVP_HAS_KIND(elem, JV_KIND_OBJECT) &&
        JVP_HAS_KIND(v, JV_KIND_OBJECT)) {
      a = jv_object_set(a, k, jv_object_merge_recursive(elem, v));  // <-- UNBOUNDED RECURSION
    } else {
      jv_free(elem);
      a = jv_object_set(a, k, v);
    }
    if (!jv_is_valid(a)) break;
  }
  jv_free(b);
  return a;
}

This is a separate finding from the recently disclosed CVE-2026-33947 (unbounded recursion in path operations) and from the jv_cmp() comparison recursion issue. Different function, different file, different trigger.

PoC

Save this as poc.jq:

reduce range(75000) as $_ ({}; {a:.}) | . * . | empty

Then run:

jq -n -f poc.jq

Result: Segmentation fault

The reduce builds an object nested 75,000 levels deep (each level is {a: <previous>}). The * operator triggers jv_object_merge_recursive() to merge the object with itself, recursing into each nested a key until the stack is exhausted.

Tested on jq 1.8.1 on x86_64 Linux. Confirmed with ASan (AddressSanitizer: stack-overflow showing repeated jv_object_merge_recursive frames) and GDB.

Impact

The * operator on objects is one of the most common ways to merge JSON in jq, used for combining configs, deep-updating nested structures, and overlaying default values. Any workflow where an attacker can influence the shape of objects being merged, or the filter expression itself, can trigger this crash. A single deep object merged with itself is enough. The crash is not exploitable for code execution on modern systems with stack guard pages, so the impact is limited to process termination.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CVE ID

CVE-2026-43896

Weaknesses

Uncontrolled Recursion

The product does not properly control the amount of recursion that takes place, consuming excessive resources, such as allocated memory or the program stack. Learn more on MITRE.

Credits