Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package jq for openSUSE:Factory checked in at 2026-05-24 19:34:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/jq (Old) and /work/SRC/openSUSE:Factory/.jq.new.2084 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "jq" Sun May 24 19:34:46 2026 rev:22 rq:1354727 version:1.8.1 Changes: -------- --- /work/SRC/openSUSE:Factory/jq/jq.changes 2025-10-04 18:51:40.350836724 +0200 +++ /work/SRC/openSUSE:Factory/.jq.new.2084/jq.changes 2026-05-24 19:35:19.430467391 +0200 @@ -1,0 +2,18 @@ +Fri May 22 10:35:53 UTC 2026 - Nathan Cutler <[email protected]> + +- Add patch CVE-2026-33948.patch (CVE-2026-33948, bsc#1262043) +- Add patch CVE-2026-32316.patch (CVE-2026-32316, bsc#1262044) +- Add patch CVE-2026-33947.patch (CVE-2026-33947, bsc#1262069) +- Add patch CVE-2026-39956.patch (CVE-2026-39956, bsc#1262070) +- Add patch CVE-2026-39979.patch (CVE-2026-39979, bsc#1262071) +- Add patch CVE-2026-40164.patch (CVE-2026-40164, bsc#1262072) +- Add patch CVE-2026-40612.patch (CVE-2026-40612, bsc#1265060) +- Add patch CVE-2026-41256.patch (CVE-2026-41256, bsc#1265061) +- Add patch CVE-2026-41257.patch (CVE-2026-41257, bsc#1265062) +- Add patch CVE-2026-43894.patch (CVE-2026-43894, bsc#1265070) +- Add patch CVE-2026-43895.patch (CVE-2026-43895, bsc#1265071) +- Add patch CVE-2026-43896.patch (CVE-2026-43896, bsc#1265075) +- Add patches CVE-2026-44777_0.patch and CVE-2026-44777_1.patch + (CVE-2026-44777, bsc#1265076) + +------------------------------------------------------------------- New: ---- CVE-2026-32316.patch CVE-2026-33947.patch CVE-2026-33948.patch CVE-2026-39956.patch CVE-2026-39979.patch CVE-2026-40164.patch CVE-2026-40612.patch CVE-2026-41256.patch CVE-2026-41257.patch CVE-2026-43894.patch CVE-2026-43895.patch CVE-2026-43896.patch CVE-2026-44777_0.patch CVE-2026-44777_1.patch ----------(New B)---------- New:- Add patch CVE-2026-33948.patch (CVE-2026-33948, bsc#1262043) - Add patch CVE-2026-32316.patch (CVE-2026-32316, bsc#1262044) - Add patch CVE-2026-33947.patch (CVE-2026-33947, bsc#1262069) New:- Add patch CVE-2026-32316.patch (CVE-2026-32316, bsc#1262044) - Add patch CVE-2026-33947.patch (CVE-2026-33947, bsc#1262069) - Add patch CVE-2026-39956.patch (CVE-2026-39956, bsc#1262070) New: - Add patch CVE-2026-33948.patch (CVE-2026-33948, bsc#1262043) - Add patch CVE-2026-32316.patch (CVE-2026-32316, bsc#1262044) New:- Add patch CVE-2026-33947.patch (CVE-2026-33947, bsc#1262069) - Add patch CVE-2026-39956.patch (CVE-2026-39956, bsc#1262070) - Add patch CVE-2026-39979.patch (CVE-2026-39979, bsc#1262071) New:- Add patch CVE-2026-39956.patch (CVE-2026-39956, bsc#1262070) - Add patch CVE-2026-39979.patch (CVE-2026-39979, bsc#1262071) - Add patch CVE-2026-40164.patch (CVE-2026-40164, bsc#1262072) New:- Add patch CVE-2026-39979.patch (CVE-2026-39979, bsc#1262071) - Add patch CVE-2026-40164.patch (CVE-2026-40164, bsc#1262072) - Add patch CVE-2026-40612.patch (CVE-2026-40612, bsc#1265060) New:- Add patch CVE-2026-40164.patch (CVE-2026-40164, bsc#1262072) - Add patch CVE-2026-40612.patch (CVE-2026-40612, bsc#1265060) - Add patch CVE-2026-41256.patch (CVE-2026-41256, bsc#1265061) New:- Add patch CVE-2026-40612.patch (CVE-2026-40612, bsc#1265060) - Add patch CVE-2026-41256.patch (CVE-2026-41256, bsc#1265061) - Add patch CVE-2026-41257.patch (CVE-2026-41257, bsc#1265062) New:- Add patch CVE-2026-41256.patch (CVE-2026-41256, bsc#1265061) - Add patch CVE-2026-41257.patch (CVE-2026-41257, bsc#1265062) - Add patch CVE-2026-43894.patch (CVE-2026-43894, bsc#1265070) New:- Add patch CVE-2026-41257.patch (CVE-2026-41257, bsc#1265062) - Add patch CVE-2026-43894.patch (CVE-2026-43894, bsc#1265070) - Add patch CVE-2026-43895.patch (CVE-2026-43895, bsc#1265071) New:- Add patch CVE-2026-43894.patch (CVE-2026-43894, bsc#1265070) - Add patch CVE-2026-43895.patch (CVE-2026-43895, bsc#1265071) - Add patch CVE-2026-43896.patch (CVE-2026-43896, bsc#1265075) New:- Add patch CVE-2026-43895.patch (CVE-2026-43895, bsc#1265071) - Add patch CVE-2026-43896.patch (CVE-2026-43896, bsc#1265075) - Add patches CVE-2026-44777_0.patch and CVE-2026-44777_1.patch New:- Add patch CVE-2026-43896.patch (CVE-2026-43896, bsc#1265075) - Add patches CVE-2026-44777_0.patch and CVE-2026-44777_1.patch (CVE-2026-44777, bsc#1265076) New:- Add patch CVE-2026-43896.patch (CVE-2026-43896, bsc#1265075) - Add patches CVE-2026-44777_0.patch and CVE-2026-44777_1.patch (CVE-2026-44777, bsc#1265076) ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ jq.spec ++++++ --- /var/tmp/diff_new_pack.VGXRLN/_old 2026-05-24 19:35:20.202498979 +0200 +++ /var/tmp/diff_new_pack.VGXRLN/_new 2026-05-24 19:35:20.206499143 +0200 @@ -1,7 +1,8 @@ # # spec file for package jq # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -25,6 +26,20 @@ Group: Productivity/Text/Utilities URL: https://github.com/jqlang Source: https://github.com/jqlang/jq/releases/download/%{name}-%{version}/%{name}-%{version}.tar.gz +Patch0: CVE-2026-33948.patch +Patch1: CVE-2026-32316.patch +Patch2: CVE-2026-33947.patch +Patch3: CVE-2026-39956.patch +Patch4: CVE-2026-39979.patch +Patch5: CVE-2026-40164.patch +Patch6: CVE-2026-40612.patch +Patch7: CVE-2026-41256.patch +Patch8: CVE-2026-41257.patch +Patch9: CVE-2026-43894.patch +Patch10: CVE-2026-43895.patch +Patch11: CVE-2026-43896.patch +Patch12: CVE-2026-44777_0.patch +Patch13: CVE-2026-44777_1.patch BuildRequires: chrpath BuildRequires: pkgconfig BuildRequires: pkgconfig(oniguruma) ++++++ CVE-2026-32316.patch ++++++ >From e47e56d226519635768e6aab2f38f0ab037c09e5 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Thu, 12 Mar 2026 20:28:43 +0900 Subject: [PATCH] Fix heap buffer overflow in `jvp_string_append` and `jvp_string_copy_replace_bad` In `jvp_string_append`, the allocation size `(currlen + len) * 2` could overflow `uint32_t` when `currlen + len` exceeds `INT_MAX`, causing a small allocation followed by a large `memcpy`. In `jvp_string_copy_replace_bad`, the output buffer size calculation `length * 3 + 1` could overflow `uint32_t`, again resulting in a small allocation followed by a large write. Add overflow checks to both functions to return an error for strings that would exceed `INT_MAX` in length. Fixes CVE-2026-32316. --- src/jv.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) Index: jq-1.8.1/src/jv.c =================================================================== --- jq-1.8.1.orig/src/jv.c +++ jq-1.8.1/src/jv.c @@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(co const char* end = data + length; const char* i = data; - uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD + // worst case: all bad bytes, each becomes a 3-byte U+FFFD + uint64_t maxlength = (uint64_t)length * 3 + 1; + if (maxlength >= INT_MAX) { + return jv_invalid_with_msg(jv_string("String too long")); + } + jvp_string* s = jvp_string_alloc(maxlength); char* out = s->data; int c = 0; @@ -1174,6 +1179,10 @@ static uint32_t jvp_string_remaining_spa static jv jvp_string_append(jv string, const char* data, uint32_t len) { jvp_string* s = jvp_string_ptr(string); uint32_t currlen = jvp_string_length(s); + if ((uint64_t)currlen + len >= INT_MAX) { + jv_free(string); + return jv_invalid_with_msg(jv_string("String too long")); + } if (jvp_refcnt_unshared(string.u.ptr) && jvp_string_remaining_space(s) >= len) { ++++++ CVE-2026-33947.patch ++++++ >From fb59f1491058d58bdc3e8dd28f1773d1ac690a1f Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 13 Apr 2026 11:23:40 +0900 Subject: [PATCH] Limit path depth to prevent stack overflow Deeply nested path arrays can cause unbounded recursion in `jv_setpath`, `jv_getpath`, and `jv_delpaths`, leading to stack overflow. Add a depth limit of 10000 to match the existing `tojson` depth limit. This fixes CVE-2026-33947. --- src/jv_aux.c | 21 +++++++++++++++++++++ tests/jq.test | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+) Index: jq-1.8.1/src/jv_aux.c =================================================================== --- jq-1.8.1.orig/src/jv_aux.c +++ jq-1.8.1/src/jv_aux.c @@ -375,6 +375,10 @@ static jv jv_dels(jv t, jv keys) { return t; } +#ifndef MAX_PATH_DEPTH +#define MAX_PATH_DEPTH (10000) +#endif + jv jv_setpath(jv root, jv path, jv value) { if (jv_get_kind(path) != JV_KIND_ARRAY) { jv_free(value); @@ -382,6 +386,12 @@ jv jv_setpath(jv root, jv path, jv value jv_free(path); return jv_invalid_with_msg(jv_string("Path must be specified as an array")); } + if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { + jv_free(value); + jv_free(root); + jv_free(path); + return jv_invalid_with_msg(jv_string("Path too deep")); + } if (!jv_is_valid(root)){ jv_free(value); jv_free(path); @@ -434,6 +444,11 @@ jv jv_getpath(jv root, jv path) { jv_free(path); return jv_invalid_with_msg(jv_string("Path must be specified as an array")); } + if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { + jv_free(root); + jv_free(path); + return jv_invalid_with_msg(jv_string("Path too deep")); + } if (!jv_is_valid(root)) { jv_free(path); return root; @@ -511,6 +526,12 @@ jv jv_delpaths(jv object, jv paths) { jv_free(elem); return err; } + if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) { + jv_free(object); + jv_free(paths); + jv_free(elem); + return jv_invalid_with_msg(jv_string("Path too deep")); + } jv_free(elem); } if (jv_array_length(jv_copy(paths)) == 0) { Index: jq-1.8.1/tests/jq.test =================================================================== --- jq-1.8.1.orig/tests/jq.test +++ jq-1.8.1/tests/jq.test @@ -2507,3 +2507,28 @@ strflocaltime("" | ., @uri) 0 "" "" + +# regression test for CVE-2026-33947 +setpath([range(10000) | 0]; 0) | flatten +null +[0] + +try setpath([range(10001) | 0]; 0) catch . +null +"Path too deep" + +getpath([range(10000) | 0]) +null +null + +try getpath([range(10001) | 0]) catch . +null +"Path too deep" + +delpaths([[range(10000) | 0]]) +null +null + +try delpaths([[range(10001) | 0]]) catch . +null +"Path too deep" ++++++ CVE-2026-33948.patch ++++++ >From 6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 13 Apr 2026 08:46:11 +0900 Subject: [PATCH] Fix NUL truncation in the JSON parser This fixes CVE-2026-33948. --- src/util.c | 8 +------- tests/shtest | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) Index: jq-1.8.1/src/util.c =================================================================== --- jq-1.8.1.orig/src/util.c +++ jq-1.8.1/src/util.c @@ -309,13 +309,7 @@ static int jq_util_input_read_more(jq_ut if (p != NULL) state->current_line++; - if (p == NULL && state->parser != NULL) { - /* - * There should be no NULs in JSON texts (but JSON text - * sequences are another story). - */ - state->buf_valid_len = strlen(state->buf); - } else if (p == NULL && feof(state->current_input)) { + if (p == NULL && feof(state->current_input)) { size_t i; /* Index: jq-1.8.1/tests/shtest =================================================================== --- jq-1.8.1.orig/tests/shtest +++ jq-1.8.1/tests/shtest @@ -842,4 +842,10 @@ if ! $msys && ! $mingw; then fi fi +# CVE-2026-33948: No NUL truncation in the JSON parser +if printf '{}\x00{}' | $JQ >/dev/null 2> /dev/null; then + printf 'Error expected but jq exited successfully\n' 1>&2 + exit 1 +fi + exit 0 ++++++ CVE-2026-39956.patch ++++++ >From fdf8ef0f0810e3d365cdd5160de43db46f57ed03 Mon Sep 17 00:00:00 2001 From: tlsbollei <[email protected]> Date: Wed, 8 Apr 2026 21:43:46 +0200 Subject: [PATCH] Add runtime type checks to f_string_indexes This fixes CVE-2026-39956. --- src/builtin.c | 8 ++++++++ tests/jq.test | 9 +++++++++ 2 files changed, 17 insertions(+) Index: jq-1.8.1/src/builtin.c =================================================================== --- jq-1.8.1.orig/src/builtin.c +++ jq-1.8.1/src/builtin.c @@ -1286,6 +1286,14 @@ static jv f_string_explode(jq_state *jq, } static jv f_string_indexes(jq_state *jq, jv a, jv b) { + if (jv_get_kind(a) != JV_KIND_STRING) { + jv_free(b); + return type_error(a, "cannot be searched, as it is not a string"); + } + if (jv_get_kind(b) != JV_KIND_STRING) { + jv_free(a); + return type_error(b, "is not a string"); + } return jv_string_indexes(a, b); } Index: jq-1.8.1/tests/jq.test =================================================================== --- jq-1.8.1.orig/tests/jq.test +++ jq-1.8.1/tests/jq.test @@ -1520,6 +1520,15 @@ split("") "xababababax" [1,7,[1,3,5,7]] +# _strindices is used by indices/1 but is callable +try _strindices("abc") catch . +123 +"number (123) cannot be searched, as it is not a string" + +try _strindices(123) catch . +"abc" +"number (123) is not a string" + # trim # \u000b is vertical tab (\v not supported by json) map(trim), map(ltrim), map(rtrim) ++++++ CVE-2026-39979.patch ++++++ >From 2f09060afab23fe9390cce7cb860b10416e1bf5f Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 13 Apr 2026 11:04:52 +0900 Subject: [PATCH] Fix out-of-bounds read in jv_parse_sized() This fixes CVE-2026-39979. Co-authored-by: Mattias Wadman <[email protected]> --- src/jv_parse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) Index: jq-1.8.1/src/jv_parse.c =================================================================== --- jq-1.8.1.orig/src/jv_parse.c +++ jq-1.8.1/src/jv_parse.c @@ -892,8 +892,9 @@ jv jv_parse_sized_custom_flags(const cha if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); - value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", + value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')", jv_string_value(msg), + length, string)); jv_free(msg); } ++++++ CVE-2026-40164.patch ++++++ >From 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 13 Apr 2026 08:53:26 +0900 Subject: [PATCH] Randomize hash seed to mitigate hash collision DoS attacks The hash function used a fixed seed, allowing attackers to craft colliding keys and cause O(n^2) object parsing performance. Initialize the seed from a random source at process startup to prevent the attack. This fixes CVE-2026-40164. Co-authored-by: Asaf Meizner <[email protected]> --- configure.ac | 2 ++ src/jv.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) Index: jq-1.8.1/configure.ac =================================================================== --- jq-1.8.1.orig/configure.ac +++ jq-1.8.1/configure.ac @@ -143,6 +143,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [ AC_CHECK_MEMBER([struct tm.__tm_gmtoff], [AC_DEFINE([HAVE_TM___TM_GMT_OFF],1,[Define to 1 if the system has the __tm_gmt_off field in struct tm])], [], [[#include <time.h>]]) AC_FIND_FUNC([setlocale], [c], [#include <locale.h>], [0,0]) +AC_FIND_FUNC([arc4random], [c], [#include <stdlib.h>], []) +AC_FIND_FUNC([getentropy], [c], [#include <unistd.h>], [0, 0]) dnl Figure out if we have the pthread functions we actually need AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include <pthread.h>], [NULL, NULL]) Index: jq-1.8.1/src/jv.c =================================================================== --- jq-1.8.1.orig/src/jv.c +++ jq-1.8.1/src/jv.c @@ -40,6 +40,10 @@ #include <limits.h> #include <math.h> #include <float.h> +#include <time.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> #include "jv_alloc.h" #include "jv.h" @@ -1206,7 +1210,33 @@ static jv jvp_string_append(jv string, c } } -static const uint32_t HASH_SEED = 0x432A9843; +static uint32_t hash_seed; +static pthread_once_t hash_seed_once = PTHREAD_ONCE_INIT; + +static void jvp_hash_seed_init(void) { + uint32_t seed; +#if defined(HAVE_ARC4RANDOM) + seed = arc4random(); +#elif defined(HAVE_GETENTROPY) + if (getentropy(&seed, sizeof(seed)) != 0) + seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); +#else + int fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + if (read(fd, &seed, sizeof(seed)) != 4) + seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); + close(fd); + } else { + seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); + } +#endif + hash_seed = seed; +} + +static uint32_t jvp_hash_seed(void) { + pthread_once(&hash_seed_once, jvp_hash_seed_init); + return hash_seed; +} static uint32_t rotl32 (uint32_t x, int8_t r){ return (x << r) | (x >> (32 - r)); @@ -1225,7 +1255,7 @@ static uint32_t jvp_string_hash(jv jstr) int len = (int)jvp_string_length(str); const int nblocks = len / 4; - uint32_t h1 = HASH_SEED; + uint32_t h1 = jvp_hash_seed(); const uint32_t c1 = 0xcc9e2d51; const uint32_t c2 = 0x1b873593; ++++++ CVE-2026-40612.patch ++++++ >From d1a12569d91641135976a8536776a4a329c02cc2 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Fri, 24 Apr 2026 22:02:24 +0900 Subject: [PATCH] Limit the containment check depth This fixes CVE-2026-40612. --- src/builtin.c | 5 ++++- src/jv.c | 40 +++++++++++++++++++++++++++------------- tests/jq.test | 9 +++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) Index: jq-1.8.1/src/builtin.c =================================================================== --- jq-1.8.1.orig/src/builtin.c +++ jq-1.8.1/src/builtin.c @@ -419,7 +419,10 @@ jv binop_greatereq(jv a, jv b) { static jv f_contains(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) == jv_get_kind(b)) { - return jv_bool(jv_contains(a, b)); + int r = jv_contains(a, b); + if (r < 0) + return jv_invalid_with_msg(jv_string("Containment check too deep")); + return jv_bool(r); } else { return type_error2(a, b, "cannot have their containment checked"); } Index: jq-1.8.1/src/jv.c =================================================================== --- jq-1.8.1.orig/src/jv.c +++ jq-1.8.1/src/jv.c @@ -938,19 +938,19 @@ static void jvp_clamp_slice_params(int l } -static int jvp_array_contains(jv a, jv b) { +static int jvp_contains(jv a, jv b, int depth); + +static int jvp_array_contains(jv a, jv b, int depth) { int r = 1; jv_array_foreach(b, bi, belem) { int ri = 0; jv_array_foreach(a, ai, aelem) { - if (jv_contains(aelem, jv_copy(belem))) { - ri = 1; - break; - } + ri = jvp_contains(aelem, jv_copy(belem), depth); + if (ri) break; } jv_free(belem); - if (!ri) { - r = 0; + if (ri <= 0) { + r = ri; break; } } @@ -1843,7 +1843,7 @@ static int jvp_object_equal(jv o1, jv o2 return len1 == len2; } -static int jvp_object_contains(jv a, jv b) { +static int jvp_object_contains(jv a, jv b, int depth) { assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); assert(JVP_HAS_KIND(b, JV_KIND_OBJECT)); int r = 1; @@ -1851,9 +1851,9 @@ static int jvp_object_contains(jv a, jv jv_object_foreach(b, key, b_val) { jv a_val = jv_object_get(jv_copy(a), key); - r = jv_contains(a_val, b_val); + r = jvp_contains(a_val, b_val, depth); - if (!r) break; + if (r <= 0) break; } return r; } @@ -2084,14 +2084,23 @@ int jv_identical(jv a, jv b) { return r; } -int jv_contains(jv a, jv b) { +#ifndef MAX_CONTAINS_DEPTH +#define MAX_CONTAINS_DEPTH (10000) +#endif + +static int jvp_contains(jv a, jv b, int depth) { + if (depth > MAX_CONTAINS_DEPTH) { + jv_free(a); + jv_free(b); + return -1; + } int r = 1; if (jv_get_kind(a) != jv_get_kind(b)) { r = 0; } else if (JVP_HAS_KIND(a, JV_KIND_OBJECT)) { - r = jvp_object_contains(a, b); + r = jvp_object_contains(a, b, depth + 1); } else if (JVP_HAS_KIND(a, JV_KIND_ARRAY)) { - r = jvp_array_contains(a, b); + r = jvp_array_contains(a, b, depth + 1); } else if (JVP_HAS_KIND(a, JV_KIND_STRING)) { int b_len = jv_string_length_bytes(jv_copy(b)); if (b_len != 0) { @@ -2107,3 +2116,8 @@ int jv_contains(jv a, jv b) { jv_free(b); return r; } + +// Returns 1 (contained), 0 (not contained), or -1 (too deep) +int jv_contains(jv a, jv b) { + return jvp_contains(a, b, 0); +} Index: jq-1.8.1/tests/jq.test =================================================================== --- jq-1.8.1.orig/tests/jq.test +++ jq-1.8.1/tests/jq.test @@ -2541,3 +2541,12 @@ null try delpaths([[range(10001) | 0]]) catch . null "Path too deep" + +# regression test for CVE-2026-40612 +reduce range(10000) as $_ ([]; [.]) | contains([[]]) +null +true + +try (reduce range(10001) as $_ ([]; [.]) as $x | $x | contains($x)) catch . +null +"Containment check too deep" ++++++ CVE-2026-41256.patch ++++++ >From 5a015deae35d19e3ebbc65db6c157a80e76df738 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Fri, 24 Apr 2026 22:15:08 +0900 Subject: [PATCH] Fix NUL truncation in program files loaded with -f This fixes CVE-2026-41256. --- src/main.c | 8 ++++++++ tests/shtest | 7 +++++++ 2 files changed, 15 insertions(+) Index: jq-1.8.1/src/main.c =================================================================== --- jq-1.8.1.orig/src/main.c +++ jq-1.8.1/src/main.c @@ -611,6 +611,14 @@ int main(int argc, char* argv[]) { ret = JQ_ERROR_SYSTEM; goto out; } + int len = jv_string_length_bytes(jv_copy(data)); + if ((size_t)len != strlen(jv_string_value(data))) { + fprintf(stderr, "jq: program file contains NUL bytes\n"); + free(program_origin); + jv_free(data); + ret = JQ_ERROR_SYSTEM; + goto out; + } jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin)))); ARGS = JV_OBJECT(jv_string("positional"), ARGS, jv_string("named"), jv_copy(program_arguments)); Index: jq-1.8.1/tests/shtest =================================================================== --- jq-1.8.1.orig/tests/shtest +++ jq-1.8.1/tests/shtest @@ -848,4 +848,11 @@ if printf '{}\x00{}' | $JQ >/dev/null 2> exit 1 fi +# CVE-2026-41256: No NUL truncation in program files loaded with -f +printf '.\x00invalid' > "$d/nul_prog.jq" +if echo '42' | $JQ -f "$d/nul_prog.jq" >/dev/null 2>/dev/null; then + printf 'Error expected for program file with NUL bytes\n' 1>&2 + exit 1 +fi + exit 0 ++++++ CVE-2026-41257.patch ++++++ >From 01b3cded76daacbfddb7f8763700b0803bcb5c6f Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Fri, 24 Apr 2026 22:09:44 +0900 Subject: [PATCH] Fix signed-int overflow in `stack_reallocate` This fixes CVE-2026-41257. --- src/exec_stack.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) Index: jq-1.8.1/src/exec_stack.h =================================================================== --- jq-1.8.1.orig/src/exec_stack.h +++ jq-1.8.1/src/exec_stack.h @@ -2,8 +2,10 @@ #define EXEC_STACK_H #include <stddef.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <limits.h> #include "jv_alloc.h" /* @@ -81,15 +83,19 @@ static stack_ptr* stack_block_next(struc } static void stack_reallocate(struct stack* s, size_t sz) { - int old_mem_length = -(s->bound) + ALIGNMENT; - char* old_mem_start = (s->mem_end != NULL) ? (s->mem_end - old_mem_length) : NULL; + size_t old_mem_length = (size_t)(-(s->bound)) + ALIGNMENT; + char* old_mem_start = s->mem_end != NULL ? s->mem_end - old_mem_length : NULL; - int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2); + size_t new_mem_length = align_round_up((old_mem_length + sz + 256) * 2); + if (new_mem_length > INT_MAX) { + fprintf(stderr, "jq: error: cannot allocate memory\n"); + abort(); + } char* new_mem_start = jv_mem_realloc(old_mem_start, new_mem_length); memmove(new_mem_start + (new_mem_length - old_mem_length), new_mem_start, old_mem_length); s->mem_end = new_mem_start + new_mem_length; - s->bound = -(new_mem_length - ALIGNMENT); + s->bound = -(int)(new_mem_length - ALIGNMENT); } static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) { ++++++ CVE-2026-43894.patch ++++++ >From 9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Wed, 6 May 2026 19:45:24 +0900 Subject: [PATCH] Reject numeric literals longer than DEC_MAX_DIGITS (999999999) A signed-int overflow in decNumber's D2U macro lets huge literals write attacker-controlled bytes past a stack buffer. Cap the length before calling decNumberFromString, and pre-slice long strings in jv_dump_string_trunc so the resulting error message doesn't itself allocate a multi-GiB buffer. Fixes CVE-2026-43894. --- src/jv.c | 5 ++++- src/jv_print.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) Index: jq-1.8.1/src/jv.c =================================================================== --- jq-1.8.1.orig/src/jv.c +++ jq-1.8.1/src/jv.c @@ -578,7 +578,10 @@ static jvp_literal_number* jvp_literal_n } static jv jvp_literal_number_new(const char * literal) { - jvp_literal_number* n = jvp_literal_number_alloc(strlen(literal)); + size_t len = strlen(literal); + if (len > DEC_MAX_DIGITS) + return JV_INVALID; + jvp_literal_number* n = jvp_literal_number_alloc(len); decContext *ctx = DEC_CONTEXT(); decContextClearStatus(ctx, DEC_Conversion_syntax); Index: jq-1.8.1/src/jv_print.c =================================================================== --- jq-1.8.1.orig/src/jv_print.c +++ jq-1.8.1/src/jv_print.c @@ -408,6 +408,10 @@ jv jv_dump_string(jv x, int flags) { } char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) { + if (jv_get_kind(x) == JV_KIND_STRING && + (size_t)jv_string_length_bytes(jv_copy(x)) > bufsize) { + x = jv_string_slice(x, 0, bufsize); + } x = jv_dump_string(x, 0); const char *str = jv_string_value(x); const size_t len = strlen(str); ++++++ CVE-2026-43895.patch ++++++ ++++ 1535 lines (skipped) ++++++ CVE-2026-43896.patch ++++++ >From 532ccea6080ed6758f39fe9f6208a44b665023d2 Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Tue, 5 May 2026 22:44:02 +0900 Subject: [PATCH] Limit recursive object merge depth to prevent stack overflow This fixes CVE-2026-43896. --- src/jv.c | 25 +++++++++++++++++++++++-- tests/jq.test | 9 +++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) Index: jq-1.8.1/src/jv.c =================================================================== --- jq-1.8.1.orig/src/jv.c +++ jq-1.8.1/src/jv.c @@ -1933,16 +1933,33 @@ jv jv_object_merge(jv a, jv b) { return a; } -jv jv_object_merge_recursive(jv a, jv b) { +#ifndef MAX_OBJECT_MERGE_DEPTH +#define MAX_OBJECT_MERGE_DEPTH (10000) +#endif + +static jv jvp_object_merge_recursive(jv a, jv b, int depth) { assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); assert(JVP_HAS_KIND(b, JV_KIND_OBJECT)); + if (depth > MAX_OBJECT_MERGE_DEPTH) { + jv_free(a); + jv_free(b); + return jv_invalid_with_msg(jv_string("Object merge too deep")); + } + 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)); + jv merged = jvp_object_merge_recursive(elem, v, depth + 1); + if (!jv_is_valid(merged)) { + jv_free(k); + jv_free(a); + jv_free(b); + return merged; + } + a = jv_object_set(a, k, merged); } else { jv_free(elem); a = jv_object_set(a, k, v); @@ -1953,6 +1970,10 @@ jv jv_object_merge_recursive(jv a, jv b) return a; } +jv jv_object_merge_recursive(jv a, jv b) { + return jvp_object_merge_recursive(a, b, 0); +} + /* * Object iteration (internal helpers) */ Index: jq-1.8.1/tests/jq.test =================================================================== --- jq-1.8.1.orig/tests/jq.test +++ jq-1.8.1/tests/jq.test @@ -2550,3 +2550,12 @@ true try (reduce range(10001) as $_ ([]; [.]) as $x | $x | contains($x)) catch . null "Containment check too deep" + +# regression test for CVE-2026-43896 +reduce range(10000) as $_ ({}; {a: .}) as $x | $x * $x | length +null +1 + +try (reduce range(10001) as $_ ({}; {a: .}) as $x | $x * $x) catch . +null +"Object merge too deep" ++++++ CVE-2026-44777_0.patch ++++++ >From f58787c41835d9b17795730cb04925fdba25c71c Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 11 May 2026 20:41:38 +0900 Subject: [PATCH] Detect circular module imports to prevent stack overflow jq used to recurse without bound on mutual or self-referential `import` declarations, exhausting the stack. Track each library's load state with a `loading` flag set before its dependencies are processed; a recursive reference to an in-progress library now reports "circular import of X". Fixes CVE-2026-44777. --- Makefile.am | 2 ++ tests/modules/cycle_a.jq | 2 ++ tests/modules/cycle_b.jq | 2 ++ tests/modules/cycle_self.jq | 2 ++ tests/shtest | 23 +++++++++++++++ 6 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 tests/modules/cycle_a.jq create mode 100644 tests/modules/cycle_b.jq create mode 100644 tests/modules/cycle_self.jq Index: jq-1.8.1/Makefile.am =================================================================== --- jq-1.8.1.orig/Makefile.am +++ jq-1.8.1/Makefile.am @@ -227,6 +227,8 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $( tests/modules/test_bind_order0.jq \ tests/modules/test_bind_order1.jq \ tests/modules/test_bind_order2.jq \ + tests/modules/cycle_a.jq tests/modules/cycle_b.jq \ + tests/modules/cycle_self.jq \ tests/onig.supp tests/local.supp \ tests/setup tests/torture/input0.json \ tests/optional.test tests/man.test tests/manonig.test \ Index: jq-1.8.1/tests/modules/cycle_a.jq =================================================================== --- /dev/null +++ jq-1.8.1/tests/modules/cycle_a.jq @@ -0,0 +1,2 @@ +import "cycle_b" as b; +def f: null; Index: jq-1.8.1/tests/modules/cycle_b.jq =================================================================== --- /dev/null +++ jq-1.8.1/tests/modules/cycle_b.jq @@ -0,0 +1,2 @@ +import "cycle_a" as a; +def f: null; Index: jq-1.8.1/tests/modules/cycle_self.jq =================================================================== --- /dev/null +++ jq-1.8.1/tests/modules/cycle_self.jq @@ -0,0 +1,2 @@ +import "cycle_self" as s; +def f: null; Index: jq-1.8.1/tests/shtest =================================================================== --- jq-1.8.1.orig/tests/shtest +++ jq-1.8.1/tests/shtest @@ -375,17 +375,40 @@ if ! HOME="$mods/home2" $VALGRIND $Q $JQ exit 1 fi +( cd "$JQBASEDIR" # so that relative library paths are guaranteed correct if ! $VALGRIND $Q $JQ -L ./tests/modules -ne 'import "test_bind_order" as check; check::check==true'; then echo "Issue #817 regression?" 1>&2 exit 1 fi +) +( cd "$JQBASEDIR" if ! $VALGRIND $Q $JQ -L tests/modules -ne 'import "test_bind_order" as check; check::check==true'; then echo "Issue #817 regression?" 1>&2 exit 1 fi +) + +# CVE-2026-44777: Circular imports should be detected +if $VALGRIND $JQ -L "$mods" -ne 'import "cycle_a" as a; null' 2> $d/out; then + echo "Mutual import should be rejected" 1>&2 + exit 1 +fi +if ! grep -q "circular import" $d/out; then + echo "Expected circular import error" 1>&2 + exit 1 +fi + +if $VALGRIND $JQ -L "$mods" -ne 'import "cycle_self" as s; null' 2> $d/out; then + echo "Self import should be rejected" 1>&2 + exit 1 +fi +if ! grep -q "circular import" $d/out; then + echo "Expected circular import error" 1>&2 + exit 1 +fi ## Halt ++++++ CVE-2026-44777_1.patch ++++++ >From f58787c41835d9b17795730cb04925fdba25c71c Mon Sep 17 00:00:00 2001 From: itchyny <[email protected]> Date: Mon, 11 May 2026 20:41:38 +0900 Subject: [PATCH] Detect circular module imports to prevent stack overflow jq used to recurse without bound on mutual or self-referential `import` declarations, exhausting the stack. Track each library's load state with a `loading` flag set before its dependencies are processed; a recursive reference to an in-progress library now reports "circular import of X". Fixes CVE-2026-44777. --- src/linker.c | 59 ++++++++++++++++++++++++------------- Index: jq-1.8.1/src/linker.c =================================================================== --- jq-1.8.1.orig/src/linker.c +++ jq-1.8.1/src/linker.c @@ -21,9 +21,13 @@ #include "compile.h" #include "jv_alloc.h" +struct lib_entry { + char *name; + block def; + int loading; +}; struct lib_loading_state { - char **names; - block *defs; + struct lib_entry *entries; uint64_t ct; }; static int load_library(jq_state *jq, jv lib_path, @@ -303,14 +307,24 @@ static int process_dependencies(jq_state } else { uint64_t state_idx = 0; for (; state_idx < lib_state->ct; ++state_idx) { - if (strcmp(lib_state->names[state_idx],jv_string_value(resolved)) == 0) + if (strcmp(lib_state->entries[state_idx].name, jv_string_value(resolved)) == 0) break; } if (state_idx < lib_state->ct) { // Found + if (lib_state->entries[state_idx].loading) { + jq_report_error(jq, jv_string_fmt("jq: error: circular import of %s\n", + jv_string_value(resolved))); + jv_free(resolved); + jv_free(as); + jv_free(deps); + jv_free(jq_origin); + jv_free(lib_origin); + return 1; + } jv_free(resolved); // Bind the library to the program - bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, as_str); + bk = block_bind_library(lib_state->entries[state_idx].def, bk, OP_IS_CALL_PSEUDO, as_str); } else { // Not found. Add it to the table before binding. block dep_def_block = gen_noop(); nerrors += load_library(jq, resolved, is_data, raw, optional, as_str, &dep_def_block, lib_state); @@ -352,30 +366,38 @@ static int load_library(jq_state *jq, jv jq_report_error(jq, jv_string_fmt("jq: error loading data file %s: %s\n", jv_string_value(lib_path), jv_string_value(data))); nerrors++; } - goto out; } else if (is_data) { // import "foo" as $bar; program = gen_const_global(jv_copy(data), as); + state_idx = lib_state->ct++; + lib_state->entries = jv_mem_realloc(lib_state->entries, lib_state->ct * sizeof(struct lib_entry)); + lib_state->entries[state_idx].name = strdup(jv_string_value(lib_path)); + lib_state->entries[state_idx].def = program; + lib_state->entries[state_idx].loading = 0; } else { // import "foo" as bar; src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data))); nerrors += jq_parse_library(src, &program); locfile_free(src); if (nerrors == 0) { + // Register the library before processing its dependencies so that + // circular imports can be detected. + state_idx = lib_state->ct++; + lib_state->entries = jv_mem_realloc(lib_state->entries, lib_state->ct * sizeof(struct lib_entry)); + lib_state->entries[state_idx].name = strdup(jv_string_value(lib_path)); + lib_state->entries[state_idx].def = gen_noop(); + lib_state->entries[state_idx].loading = 1; + char *lib_origin = strdup(jv_string_value(lib_path)); nerrors += process_dependencies(jq, jq_get_jq_origin(jq), jv_string(dirname(lib_origin)), &program, lib_state); free(lib_origin); program = block_bind_self(program, OP_IS_CALL_PSEUDO); + lib_state->entries[state_idx].def = program; + lib_state->entries[state_idx].loading = 0; } } - state_idx = lib_state->ct++; - lib_state->names = jv_mem_realloc(lib_state->names, lib_state->ct * sizeof(const char *)); - lib_state->defs = jv_mem_realloc(lib_state->defs, lib_state->ct * sizeof(block)); - lib_state->names[state_idx] = strdup(jv_string_value(lib_path)); - lib_state->defs[state_idx] = program; -out: *out_block = program; jv_free(lib_path); jv_free(data); @@ -413,7 +435,7 @@ jv load_module_meta(jq_state *jq, jv mod int load_program(jq_state *jq, struct locfile* src, block *out_block) { int nerrors = 0; block program; - struct lib_loading_state lib_state = {0,0,0}; + struct lib_loading_state lib_state = {0,0}; nerrors = jq_parse(src, &program); if (nerrors) return nerrors; @@ -439,14 +461,13 @@ int load_program(jq_state *jq, struct lo nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state); block libs = gen_noop(); for (uint64_t i = 0; i < lib_state.ct; ++i) { - free(lib_state.names[i]); - if (nerrors == 0 && !block_is_const(lib_state.defs[i])) - libs = block_join(libs, lib_state.defs[i]); + free(lib_state.entries[i].name); + if (nerrors == 0 && !block_is_const(lib_state.entries[i].def)) + libs = block_join(libs, lib_state.entries[i].def); else - block_free(lib_state.defs[i]); + block_free(lib_state.entries[i].def); } - free(lib_state.names); - free(lib_state.defs); + free(lib_state.entries); if (nerrors) block_free(program); else
