Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions SPECS/jq/CVE-2026-32316.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
From c148852ce2a62578a8f7323f99a8d468317f93d1 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
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.

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5.patch
---
src/jv.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/jv.c b/src/jv.c
index 18dbb54..73387d8 100644
--- a/src/jv.c
+++ b/src/jv.c
@@ -1091,7 +1091,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;
@@ -1151,6 +1156,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) {
--
2.45.4

109 changes: 109 additions & 0 deletions SPECS/jq/CVE-2026-33947.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
From d4d56326d2709421cd7e0b41b2ad4574b7f98fc0 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
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.

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f.patch
---
src/jv_aux.c | 21 +++++++++++++++++++++
tests/jq.test | 25 +++++++++++++++++++++++++
2 files changed, 46 insertions(+)

diff --git a/src/jv_aux.c b/src/jv_aux.c
index bbe1c0d..0855053 100644
--- a/src/jv_aux.c
+++ b/src/jv_aux.c
@@ -376,6 +376,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);
@@ -383,6 +387,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) {
diff --git a/tests/jq.test b/tests/jq.test
index 500e741..758a161 100644
--- a/tests/jq.test
+++ b/tests/jq.test
@@ -1813,6 +1813,31 @@ all(builtins[] / "/"; .[1]|tonumber >= 0)
null
true

+# 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"
+
builtins|any(.[:1] == "_")
null
false
--
2.45.4

51 changes: 51 additions & 0 deletions SPECS/jq/CVE-2026-33948.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
From 6b00368687afbad32cb454a1cddb4af18ae2eb8b Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
Date: Mon, 13 Apr 2026 08:46:11 +0900
Subject: [PATCH] Fix NUL truncation in the JSON parser

This fixes CVE-2026-33948.

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b.patch
---
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 de44fa6..422a8b8 100644
--- a/src/util.c
+++ b/src/util.c
@@ -311,13 +311,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 a471889..0397ca0 100755
--- a/tests/shtest
+++ b/tests/shtest
@@ -609,4 +609,10 @@ $VALGRIND $Q $JQ . <<\NUM
-10E-1000000001
NUM

+# 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
--
2.45.4

35 changes: 35 additions & 0 deletions SPECS/jq/CVE-2026-39956.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
From e5c82a886d941ce534dfa1694b0d5e8f364a90fc Mon Sep 17 00:00:00 2001
From: tlsbollei <170938166+tlsbollei@users.noreply.github.com>
Date: Wed, 8 Apr 2026 21:43:46 +0200
Subject: [PATCH] Add runtime type checks to f_string_indexes

This fixes CVE-2026-39956.

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/fdf8ef0f0810e3d365cdd5160de43db46f57ed03.patch
---
src/builtin.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/src/builtin.c b/src/builtin.c
index 902490d..3cb8eb7 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -1212,6 +1212,14 @@ static jv f_string_explode(jq_state *jq, jv a) {
}

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);
}

--
2.45.4

32 changes: 32 additions & 0 deletions SPECS/jq/CVE-2026-39979.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
From 504ac57fa642aebc7d8652bade76eb1fc578a020 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
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 <mattias.wadman@gmail.com>
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f.patch
---
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 9755b8a..84a847f 100644
--- a/src/jv_parse.c
+++ b/src/jv_parse.c
@@ -890,8 +890,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);
}
--
2.45.4

92 changes: 92 additions & 0 deletions SPECS/jq/CVE-2026-40164.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
From 08d00ef18394e7cf5b1860224af821c6000e5858 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
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 <asafmeizner@gmail.com>
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784.patch
---
configure.ac | 2 ++
src/jv.c | 34 ++++++++++++++++++++++++++++++++--
2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 118e084..7f12b52 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 <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])
diff --git a/src/jv.c b/src/jv.c
index 73387d8..08ded35 100644
--- a/src/jv.c
+++ b/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"
@@ -1183,7 +1187,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));
@@ -1202,7 +1232,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;
--
2.45.4

Loading
Loading