From be36545b301bb7e4e0f46dd949db092a30a963d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Camille=20Favier?= Date: Tue, 14 Apr 2026 17:34:59 +0200 Subject: [PATCH] jq: apply CVE patches Apply upstream patches for CVE-2026-32316, CVE-2026-33947, CVE-2026-33948, CVE-2026-39979, and CVE-2026-40164. --- pkgs/by-name/jq/jq/CVE-2026-32316.patch | 49 ++++++++++++++ pkgs/by-name/jq/jq/CVE-2026-33947.patch | 66 +++++++++++++++++++ pkgs/by-name/jq/jq/CVE-2026-33948.patch | 45 +++++++++++++ pkgs/by-name/jq/jq/CVE-2026-39979.patch | 27 ++++++++ pkgs/by-name/jq/jq/CVE-2026-40164.patch | 87 +++++++++++++++++++++++++ pkgs/by-name/jq/jq/package.nix | 5 ++ 6 files changed, 279 insertions(+) create mode 100644 pkgs/by-name/jq/jq/CVE-2026-32316.patch create mode 100644 pkgs/by-name/jq/jq/CVE-2026-33947.patch create mode 100644 pkgs/by-name/jq/jq/CVE-2026-33948.patch create mode 100644 pkgs/by-name/jq/jq/CVE-2026-39979.patch create mode 100644 pkgs/by-name/jq/jq/CVE-2026-40164.patch diff --git a/pkgs/by-name/jq/jq/CVE-2026-32316.patch b/pkgs/by-name/jq/jq/CVE-2026-32316.patch new file mode 100644 index 000000000000..29d08c9adcd7 --- /dev/null +++ b/pkgs/by-name/jq/jq/CVE-2026-32316.patch @@ -0,0 +1,49 @@ +From e47e56d226519635768e6aab2f38f0ab037c09e5 Mon Sep 17 00:00:00 2001 +From: itchyny +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(-) + +diff --git a/src/jv.c b/src/jv.c +index 722a539391..2a62b48419 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { + 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_space(jvp_string* s) { + 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) { diff --git a/pkgs/by-name/jq/jq/CVE-2026-33947.patch b/pkgs/by-name/jq/jq/CVE-2026-33947.patch new file mode 100644 index 000000000000..9f31fd82b057 --- /dev/null +++ b/pkgs/by-name/jq/jq/CVE-2026-33947.patch @@ -0,0 +1,66 @@ +From fb59f1491058d58bdc3e8dd28f1773d1ac690a1f Mon Sep 17 00:00:00 2001 +From: itchyny +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 +++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/src/jv_aux.c b/src/jv_aux.c +index 018f380b10..fd5ff96684 100644 +--- a/src/jv_aux.c ++++ b/src/jv_aux.c +@@ -365,6 +365,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); +@@ -372,6 +376,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); +@@ -424,6 +434,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; +@@ -502,6 +517,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) { diff --git a/pkgs/by-name/jq/jq/CVE-2026-33948.patch b/pkgs/by-name/jq/jq/CVE-2026-33948.patch new file mode 100644 index 000000000000..5b6497e1469d --- /dev/null +++ b/pkgs/by-name/jq/jq/CVE-2026-33948.patch @@ -0,0 +1,45 @@ +From 6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b Mon Sep 17 00:00:00 2001 +From: itchyny +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(-) + +diff --git a/src/util.c b/src/util.c +index fdfdb96d88..80d65fc808 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -312,13 +312,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) { + 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; + + /* +diff --git a/tests/shtest b/tests/shtest +index cb88745277..370f7b7c69 100755 +--- a/tests/shtest ++++ b/tests/shtest +@@ -880,4 +880,10 @@ $JQ -nf $d/prog.jq 2> $d/out && { + } + diff $d/out $d/expected + ++# 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 diff --git a/pkgs/by-name/jq/jq/CVE-2026-39979.patch b/pkgs/by-name/jq/jq/CVE-2026-39979.patch new file mode 100644 index 000000000000..cc0c3d17f77f --- /dev/null +++ b/pkgs/by-name/jq/jq/CVE-2026-39979.patch @@ -0,0 +1,27 @@ +From 2f09060afab23fe9390cce7cb860b10416e1bf5f Mon Sep 17 00:00:00 2001 +From: itchyny +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 +--- + src/jv_parse.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/jv_parse.c b/src/jv_parse.c +index aa2054cc09..56847b5eaa 100644 +--- a/src/jv_parse.c ++++ b/src/jv_parse.c +@@ -893,8 +893,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) { + + 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); + } diff --git a/pkgs/by-name/jq/jq/CVE-2026-40164.patch b/pkgs/by-name/jq/jq/CVE-2026-40164.patch new file mode 100644 index 000000000000..483316f64b4b --- /dev/null +++ b/pkgs/by-name/jq/jq/CVE-2026-40164.patch @@ -0,0 +1,87 @@ +From 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 Mon Sep 17 00:00:00 2001 +From: itchyny +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 +--- + configure.ac | 2 ++ + src/jv.c | 34 ++++++++++++++++++++++++++++++++-- + 2 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 5dac42655a..f7067a4341 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -149,6 +149,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define + 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 ]]) + AC_FIND_FUNC([setlocale], [c], [#include ], [0,0]) ++AC_FIND_FUNC([arc4random], [c], [#include ], []) ++AC_FIND_FUNC([getentropy], [c], [#include ], [0, 0]) + + dnl Figure out if we have the pthread functions we actually need + AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include ], [NULL, NULL]) +diff --git a/src/jv.c b/src/jv.c +index 2a62b48419..607ac174f7 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -40,6 +40,10 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + + #include "jv_alloc.h" + #include "jv.h" +@@ -1206,7 +1210,33 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { + } + } + +-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; diff --git a/pkgs/by-name/jq/jq/package.nix b/pkgs/by-name/jq/jq/package.nix index b2a0941a79fe..31633d27ba03 100644 --- a/pkgs/by-name/jq/jq/package.nix +++ b/pkgs/by-name/jq/jq/package.nix @@ -32,6 +32,11 @@ stdenv.mkDerivation (finalAttrs: { patches = [ ./musl.patch + ./CVE-2026-32316.patch + ./CVE-2026-33947.patch + ./CVE-2026-33948.patch + ./CVE-2026-39979.patch + ./CVE-2026-40164.patch ] ++ lib.optionals stdenv.hostPlatform.is32bit [ # needed because epoch conversion test here is right at the end of 32 bit integer space