7 vulnerabilities have been posted to https://github.com/jqlang/jq/security -
6 this week and one in March that I don't see in the list archives.  Fixes
appear to be available in their github repo, but not yet in a release.

https://github.com/jqlang/jq/security/advisories/GHSA-q3h9-m34w-h76f reports:
Integer overflow in jvp_string_append and jvp_string_copy_replace_bad allows
heap buffer overflow

Affected versions: 1.8.1

Summary
-------
An integer overflow in jvp_string_append() in src/jv.c causes a heap buffer
overflow when concatenating strings whose combined length exceeds 2^31 bytes.

Details
-------
The internal function jvp_string_append() computes the new buffer allocation
size as:

    // src/jv.c:1187
    uint32_t allocsz = (currlen + len) * 2;
    if (allocsz < 32) allocsz = 32;

Both currlen and len are uint32_t. When currlen + len >= 2^31, the
multiplication by 2 overflows the 32-bit unsigned integer, wrapping allocsz to
a small value (potentially 0, clamped to 32). The subsequent memcpy operations
copy currlen + len bytes into the undersized buffer, causing a heap buffer
overflow. The length_hashed field ((currlen + len) << 1) suffers the same
overflow, corrupting the stored string length.

Arrays and objects already have size limits, but strings have no equivalent
bounds check. We have to limit the size of string so that the length does not
overflow.

Similar vulnerability exists in jvp_string_copy_replace_bad, too.

[See GHSA for PoC]

Impact
------
CWE-190 (Integer Overflow or Wraparound) leading to
CWE-122 (Heap-based Buffer Overflow).

Any user or system that evaluates untrusted jq queries is affected. Attacker
can easily crash the jq process by sending a crafted jq query. If the system
uses a fixed jq query (that does not concatenate the input strings many times),
attacker needs to send a huge JSON input to make it crash. Heap corruption may
allow further exploitation depending on the allocator and environment.
All versions of jq through 1.8.1 and current master version are affected.

Fix
---
This issue was fixed by
https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5

Severity: High - 8.2 / 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H
CVE ID: CVE-2026-32316

https://github.com/jqlang/jq/security/advisories/GHSA-2hhh-px8h-355p says:
Out-of-Bounds Read in jv_parse_sized() Error Formatting for Non-NUL-Terminated
Counted Buffers

Summary
-------
libjq exposes jv_parse_sized(const char *string, int length) as a counted-buffer
JSON parsing API, but its parse-error path later treats the same buffer as a
NUL-terminated C string. If a caller passes malformed JSON in a
non-NUL-terminated buffer, the error construction logic can read past the
caller-supplied length, causing an out-of-bounds read.

Details
-------
The vulnerable path is:

jv_parse_sized()
 -> jv_parse_sized_custom_flags()
  -> jv_parser_set_buf(&parser, string, length, 0)
   -> parse failure
    -> jv_string_fmt("%s (while parsing '%s')", ..., string)

Relevant code:

    src/jv.h (line 245)
    src/jv_parse.c (line 865)
    src/jv_parse.c (line 896)
    src/jv.c (line 1528)

The parser correctly accepts a (pointer, length) pair, but when building the
error message it formats string with %s, which causes vsnprintf() to continue
reading memory until a \0 is found. This makes the error path ignore the
explicit buffer length and turns a counted-buffer API into an unbounded read
sink.

Reachability:

Real external source: any libjq consumer calling jv_parse_sized() with a
counted buffer.

In-project internal uses such as fromjson and lexer paths also reach
jv_parse_sized(), but those pass jq-managed strings that are already
NUL-terminated, so the practical attack surface is the public API rather
than the normal jq CLI path.

[See GHSA for PoC]

Impact
------

Only libjq is affected. A caller that uses jv_parse_sized() on untrusted
malformed JSON in a non-NUL-terminated buffer can trigger an out-of-bounds
read during error construction. Depending on memory layout and how the
returned error string is logged or exposed, this can lead to memory disclosure
or process termination.

Severity:   Moderate
CVE ID:     CVE-2026-39979
Weaknesses: CWE-125 Out-of-bounds Read
Credits:    @HO-9 Reporter

https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f
states that it fixes CVE-2026-39979.


https://github.com/jqlang/jq/security/advisories/GHSA-32cx-cvvh-2wj9 advises:
Embedded-NUL Truncation in jq CLI JSON Input Path Causes Prefix-Only Validation
of Malformed Input

Summary
-------
The normal jq CLI JSON input path uses fgets() and then derives the valid byte
length with strlen() when parsing JSON input without a newline. If the input
contains an embedded NUL byte, jq truncates the already-read buffer at the NUL
and passes only the benign prefix to the JSON parser. As a result, jq may
accept malformed input by validating only the prefix before the NUL.

Details
-------
The reachable CLI path is:

CLI file/stdin input
 -> jq_util_input_add_input() / stdin default
  -> jq_util_input_set_parser(..., jv_parser_new(...))
   -> jq_util_input_next_input() -> jq_util_input_read_more()
    -> strlen(state->buf)
     -> jv_parser_set_buf(state->parser, state->buf, state->buf_valid_len,
                          !is_last)

Relevant code:

    src/main.c (line 361)
    src/main.c (line 653)
    src/main.c (line 664)
    src/main.c (line 671)
    src/util.c (line 315)
    src/util.c (line 320)
    src/util.c (line 432)

The flaw is that the code does not use the actual number of bytes read by
fgets(). Instead it uses strlen(state->buf), which stops at the first embedded
NUL. Trailing bytes after the NUL may already have been consumed from the
input stream, but they are silently excluded from parsing.

This is realistically reachable because it affects the stock jq CLI file
and stdin parsing path used by end users.

[See GHSA for PoC]

Observed error:

jq: Bad JSON in --slurpfile ...: Invalid numeric literal at line 1, column 17

Impact

This issue can cause validation bypass in workflows that rely on jq to validate
untrusted JSON before forwarding, storing, or otherwise acting on the original
bytes. An attacker can place a benign JSON prefix before an embedded NUL and
append malicious trailing data after it. jq may accept the prefix as valid
JSON while silently ignoring the suffix, creating a parser differential
between jq and downstream components that process the full input.

Severity:   Low
CVE ID:     CVE-2026-33948
Weaknesses: No CWEs
Credits:    @HO-9

https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b
says it fixes CVE-2026-33948.


https://github.com/jqlang/jq/security/advisories/GHSA-xwrw-4f8h-rjvg states:
Unbounded Recursion in jv_setpath() / jv_getpath() / delpaths_sorted()

Affected versions:  <= 1.8.1

Summary
-------
The jv_setpath(), jv_getpath(), and delpaths_sorted() functions in
src/jv_aux.c use unbounded recursion where the recursion depth equals the
length of a caller-supplied path array. There is no depth limit check.
When a path array with ~60,000 or more elements is supplied — either constructed
by a jq filter expression or provided directly in attacker-controlled JSON input
— the C call stack is exhausted, causing a segmentation fault (SIGSEGV) and
immediate process crash.

This vulnerability bypasses the MAX_PARSING_DEPTH (10,000) limit that protects
the JSON parser, because path arrays can be constructed programmatically to
arbitrary lengths without being constrained by parsing depth. Critically, the
path array can be sourced entirely from attacker-controlled JSON input, making
this exploitable in scenarios where a trusted jq filter processes untrusted 
data.

[See GHSA for code analysis and PoC]

Impact
------
- Denial of Service (Crash): Any jq process that calls setpath, getpath, or
  delpaths with a sufficiently long path array will crash with SIGSEGV.
  This is an unrecoverable crash — no error handling is possible.
- Bypass of existing depth limits: The JSON parser's MAX_PARSING_DEPTH (10,000)
  does not protect against this because path arrays are constructed at the jq
  runtime level, not during JSON parsing. An attacker can embed a flat array
  of 65,000 integers in a JSON document (only ~200 KB) that causes a crash
  when used as a path.
- Affected real-world scenarios:
  - Web services using jq to transform or extract data from user-submitted JSON
  - CI/CD pipelines processing untrusted configuration or API responses with jq
  - Shell scripts that use setpath/getpath/delpaths on paths derived from input
    data
  - Any application embedding libjq where path arguments can be influenced by
    external input
- Note: Unlike memory corruption vulnerabilities, stack overflow from recursion
  is generally not exploitable for code execution on modern systems with guard
  pages. The impact is limited to denial of service.

Severity: Moderate - 6.2 / 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVE ID: CVE-2026-33947
Weaknesses: CWE-674 Uncontrolled Recursion
Credits: @bg0d-glitch

https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f
declares that it fixes CVE-2026-33947.


https://github.com/jqlang/jq/security/advisories/GHSA-6gc3-3g9p-xx28 announces:
jq _strindices missing runtime type checks lead to crash and limited memory
disclosure

Affected versions: 1.8.1-dev commit 69785bf

Summary
-------
_strindices is the C builtin used by indices / index / rindex for string inputs.
Its wrapper passes both arguments straight to jv_string_indexes() without
checking that they are strings, and jv_string_indexes() itself only uses
assert(). In release builds those checks disappear under -DNDEBUG.
_strindices(0) is enough to crash jq, and the same bug also gives an attacker
controlled pointer dereference and a limited read/probe primitive.

Details
-------
commit 69785bf77f86e2ea1b4a20ca86775916889e91c9, _strindices is implemented
in src/builtin.c like this

    static jv f_string_indexes(jq_state *jq, jv a, jv b) {
      return jv_string_indexes(a, b);
    }

jv_string_indexes() in src/jv.c then blindly assumes both given args are 
strings:

    jv jv_string_indexes(jv j, jv k) {
      assert(JVP_HAS_KIND(j, JV_KIND_STRING));
      assert(JVP_HAS_KIND(k, JV_KIND_STRING));
      const char *jstr = jv_string_value(j);
      const char *idxstr = jv_string_value(k);
    }

In a debug build the assertions fire. In a normal release build they are
compiled out, and jq dereferences j.u.ptr as if it were a valid jvp_string *.

When the assertions are gone, jv_string_value() treats j.u.ptr as a string
header and jv_string_length_bytes() reads a length from that same fake object.
This results in either 1. an easy crash with invalid input 2. a limited read
primitive when a crafted number is used so its bit pattern is treated as a
pointer. The number being crafted matters because default jq builds use decnum.
A plain numeric literal doesn't give control of u.ptr, but arithmetic such as
this does: (<bit-cast double> + 0) reaches jv_number(double) and stores the
IEEE-754 bits of that double in the same union field which is later read as 
u.ptr

PoC
---
Crash:
   jq -n '_strindices(0)'

Controlled pointer dereference:

    import struct, subprocess

    def as_double(u64):
        return struct.unpack("<d", struct.pack("<Q", u64))[0]

    # because we want jstr = addr, the fake jvp_string sits 16 bytes earlier
    addr = 0x4141414141414151
    expr = f"({as_double(addr - 16)!r} + 0) | _strindices(\"\\u0000\")"
    result = subprocess.run(["jq", "-n", expr])
    print(result.returncode)  # sigsegv in python

Impact
------

Anything that runs untrusted jq filters against a release build can be crashed
very easily. If the system uses a fixed jq query (such that does not use the
internal _strindices filter), it is not affected. The same bug also gives
limited read behavior because values which are not strings can be treated as
fake string objects and walked by _strindices. In a real deployment that means
an attacker can use the exit status as a mapped/unmapped probe, and in favorable
cases can get some bytes back through the _strindices position output.

Severity: Moderate - 6.1 / 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H
CVE ID: CVE-2026-39956
Weaknesses:
 CWE-125 Out-of-bounds Read
 CWE-476 NULL Pointer Dereference
 CWE-843 Access of Resource Using Incompatible Type ('Type Confusion')
Credits: @tlsbollei Reporter

https://github.com/jqlang/jq/commit/fdf8ef0f0810e3d365cdd5160de43db46f57ed03
claims to fix CVE-2026-39956.


https://github.com/jqlang/jq/security/advisories/GHSA-wwj8-gxm6-jc29 expresses:
Algorithmic complexity DoS via hardcoded MurmurHash3 seed

Summary
-------
jq uses MurmurHash3 with a compile-time constant seed 0x432A9843 (src/jv.c:1200)
for all JSON object hash table operations. Since the seed is hardcoded and
publicly visible in source, an attacker can precompute hash collisions offline
and construct a JSON object where all keys hash to the same bucket.
This degrades operations from O(1) to O(n), making any jq expression O(n^2).
Only ~100KB of crafted JSON needed — far more practical than the heap overflow
issues.

Details
-------
File: src/jv.c, line 1200

    static const uint32_t HASH_SEED = 0x432A9843;

Used at line 1219:

    static uint32_t jvp_val_hash(jv val) {
        uint32_t h1 = HASH_SEED;
        // ... MurmurHash3 body ...
    }

Many languages randomize hash seeds at startup to prevent this (Python 3.3+,
Ruby 1.9+, Perl 5.18+). jq does not.

[See GHSA for PoC]

Severity: High - 7.5 / 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVE ID: CVE-2026-40164
Weaknesses:
 CWE-328 Use of Weak Hash
 CWE-407 Inefficient Algorithmic Complexity
Credits: @AsafMeizner Reporter

https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784
appears to mitigate this, but does not list the CVE id.


https://github.com/jqlang/jq/security/advisories/GHSA-gf4g-95wj-4q4r discloses:
jq args2obj() Heap-Use-After-Free Vulnerability Report

Affected versions: 1.8.1 (commit ref:b33a763)

Summary
-------
A potential heap-use-after-free vulnerability exists in jq's args2obj()
function at src/execute.c:1218. The bug is in the public jq_compile_args()
API's array argument processing path: when called with an array of 2+ named
argument entries, freed heap memory is read (CWE-416); with 1+ entries, a
double-free occurs (CWE-415). The standard jq CLI binary is not affected,
as it always passes an object (not an array) to args2obj(). It appears the
bug was introduced in commit b279713e (2017-02-26) and affects jq HEAD
(jq-1.8.1-32-gb33a763).

Details
-------
Root cause: The strings kk ("name") and vk ("value") are allocated once before
the loop but passed directly to jv_object_get(), which consumes (frees) both
its arguments. On the first iteration, kk and vk are freed. On subsequent
iterations, they are dangling pointers — jvp_string_hash() reads from freed
heap memory. After the loop, jv_free(kk) / jv_free(vk) trigger a double-free.

    // src/execute.c:1208-1223
    jv kk = jv_string("name");
    jv vk = jv_string("value");
    jv_array_foreach(args, i, v)
      r = jv_object_set(r, jv_object_get(jv_copy(v), kk), jv_object_get(v, vk));
    jv_free(kk);   // double-free: already freed by jv_object_get()
    jv_free(vk);

Git history: Commit b279713e refactored inline array processing in
jq_compile_args() into the args2obj() helper. The original code created fresh
jv_string("name") / jv_string("value") on each iteration; the refactored
version hoisted them into locals but failed to account for jv_object_get()
consuming its key argument.

[See GHSA for PoC]

Impact

CWE-416 (Use After Free) / CWE-415 (Double Free). The vulnerability is
reachable through the public jq_compile_args() API when called with array
arguments — not through the CLI binary. Heap corruption from the UAF and
double-free may lead to arbitrary code execution via corrupted heap metadata,
though no exploit has been demonstrated. All downstream consumers of
jq_compile_args() that pass array arguments have been affected since
commit b279713e (2017-02-26).

My name is Scott Seal, and I work at Trail of Bits. Per the instructions of
my employer, I am required to provide the following disclosure:

    This bug was found as part of follow-on research from DARPA's AI Cyber
    Challenge (AIXCC), where Trail of Bits built Buttercup, a Cyber Reasoning
    System that combines static analysis, fuzzing, and large language models
    to find and fix vulnerabilities.

    https://www.darpa.mil/research/programs/ai-cyber
    https://www.trailofbits.com/buttercup/

Severity: Low
CVE ID: No known CVE
Weaknesses
 CWE-415 Double Free
 CWE-416 Use After Free
Credits: @sseal Reporter

https://github.com/jqlang/jq/commit/3985b80ce50bd75c6eb5a97cb3348c3f835ca8e0
includes "Fixes GHSA-gf4g-95wj-4q4r."

--
        -Alan Coopersmith-                 [email protected]
         Oracle Solaris Engineering - https://blogs.oracle.com/solaris

Reply via email to