Skip to content

Commit 86b3d92

Browse files
CBL-Mariner-Botazurelinux-securityjslobodzian
authored
Merge PR "[AUTO-CHERRYPICK] [AutoPR- Security] Patch jq for CVE-2026-40164, CVE-2026-39979, CVE-2026-39956, CVE-2026-33948, CVE-2026-33947, CVE-2026-32316 [HIGH] - branch 3.0-dev" #16727
Co-authored-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> Co-authored-by: jslobodzian <joslobo@microsoft.com>
1 parent 535788a commit 86b3d92

File tree

7 files changed

+384
-1
lines changed

7 files changed

+384
-1
lines changed

SPECS/jq/CVE-2026-32316.patch

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
From c148852ce2a62578a8f7323f99a8d468317f93d1 Mon Sep 17 00:00:00 2001
2+
From: itchyny <itchyny@cybozu.co.jp>
3+
Date: Thu, 12 Mar 2026 20:28:43 +0900
4+
Subject: [PATCH] Fix heap buffer overflow in `jvp_string_append` and
5+
`jvp_string_copy_replace_bad`
6+
7+
In `jvp_string_append`, the allocation size `(currlen + len) * 2` could
8+
overflow `uint32_t` when `currlen + len` exceeds `INT_MAX`, causing a small
9+
allocation followed by a large `memcpy`.
10+
11+
In `jvp_string_copy_replace_bad`, the output buffer size calculation
12+
`length * 3 + 1` could overflow `uint32_t`, again resulting in a small
13+
allocation followed by a large write.
14+
15+
Add overflow checks to both functions to return an error for strings
16+
that would exceed `INT_MAX` in length. Fixes CVE-2026-32316.
17+
18+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
19+
Upstream-reference: https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5.patch
20+
---
21+
src/jv.c | 11 ++++++++++-
22+
1 file changed, 10 insertions(+), 1 deletion(-)
23+
24+
diff --git a/src/jv.c b/src/jv.c
25+
index 18dbb54..73387d8 100644
26+
--- a/src/jv.c
27+
+++ b/src/jv.c
28+
@@ -1091,7 +1091,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {
29+
const char* end = data + length;
30+
const char* i = data;
31+
32+
- uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
33+
+ // worst case: all bad bytes, each becomes a 3-byte U+FFFD
34+
+ uint64_t maxlength = (uint64_t)length * 3 + 1;
35+
+ if (maxlength >= INT_MAX) {
36+
+ return jv_invalid_with_msg(jv_string("String too long"));
37+
+ }
38+
+
39+
jvp_string* s = jvp_string_alloc(maxlength);
40+
char* out = s->data;
41+
int c = 0;
42+
@@ -1151,6 +1156,10 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) {
43+
static jv jvp_string_append(jv string, const char* data, uint32_t len) {
44+
jvp_string* s = jvp_string_ptr(string);
45+
uint32_t currlen = jvp_string_length(s);
46+
+ if ((uint64_t)currlen + len >= INT_MAX) {
47+
+ jv_free(string);
48+
+ return jv_invalid_with_msg(jv_string("String too long"));
49+
+ }
50+
51+
if (jvp_refcnt_unshared(string.u.ptr) &&
52+
jvp_string_remaining_space(s) >= len) {
53+
--
54+
2.45.4
55+

SPECS/jq/CVE-2026-33947.patch

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
From d4d56326d2709421cd7e0b41b2ad4574b7f98fc0 Mon Sep 17 00:00:00 2001
2+
From: itchyny <itchyny@cybozu.co.jp>
3+
Date: Mon, 13 Apr 2026 11:23:40 +0900
4+
Subject: [PATCH] Limit path depth to prevent stack overflow
5+
6+
Deeply nested path arrays can cause unbounded recursion in
7+
`jv_setpath`, `jv_getpath`, and `jv_delpaths`, leading to
8+
stack overflow. Add a depth limit of 10000 to match the
9+
existing `tojson` depth limit. This fixes CVE-2026-33947.
10+
11+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
12+
Upstream-reference: https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f.patch
13+
---
14+
src/jv_aux.c | 21 +++++++++++++++++++++
15+
tests/jq.test | 25 +++++++++++++++++++++++++
16+
2 files changed, 46 insertions(+)
17+
18+
diff --git a/src/jv_aux.c b/src/jv_aux.c
19+
index bbe1c0d..0855053 100644
20+
--- a/src/jv_aux.c
21+
+++ b/src/jv_aux.c
22+
@@ -376,6 +376,10 @@ static jv jv_dels(jv t, jv keys) {
23+
return t;
24+
}
25+
26+
+#ifndef MAX_PATH_DEPTH
27+
+#define MAX_PATH_DEPTH (10000)
28+
+#endif
29+
+
30+
jv jv_setpath(jv root, jv path, jv value) {
31+
if (jv_get_kind(path) != JV_KIND_ARRAY) {
32+
jv_free(value);
33+
@@ -383,6 +387,12 @@ jv jv_setpath(jv root, jv path, jv value) {
34+
jv_free(path);
35+
return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
36+
}
37+
+ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
38+
+ jv_free(value);
39+
+ jv_free(root);
40+
+ jv_free(path);
41+
+ return jv_invalid_with_msg(jv_string("Path too deep"));
42+
+ }
43+
if (!jv_is_valid(root)){
44+
jv_free(value);
45+
jv_free(path);
46+
@@ -434,6 +444,11 @@ jv jv_getpath(jv root, jv path) {
47+
jv_free(path);
48+
return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
49+
}
50+
+ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) {
51+
+ jv_free(root);
52+
+ jv_free(path);
53+
+ return jv_invalid_with_msg(jv_string("Path too deep"));
54+
+ }
55+
if (!jv_is_valid(root)) {
56+
jv_free(path);
57+
return root;
58+
@@ -511,6 +526,12 @@ jv jv_delpaths(jv object, jv paths) {
59+
jv_free(elem);
60+
return err;
61+
}
62+
+ if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) {
63+
+ jv_free(object);
64+
+ jv_free(paths);
65+
+ jv_free(elem);
66+
+ return jv_invalid_with_msg(jv_string("Path too deep"));
67+
+ }
68+
jv_free(elem);
69+
}
70+
if (jv_array_length(jv_copy(paths)) == 0) {
71+
diff --git a/tests/jq.test b/tests/jq.test
72+
index 500e741..758a161 100644
73+
--- a/tests/jq.test
74+
+++ b/tests/jq.test
75+
@@ -1813,6 +1813,31 @@ all(builtins[] / "/"; .[1]|tonumber >= 0)
76+
null
77+
true
78+
79+
+# regression test for CVE-2026-33947
80+
+setpath([range(10000) | 0]; 0) | flatten
81+
+null
82+
+[0]
83+
+
84+
+try setpath([range(10001) | 0]; 0) catch .
85+
+null
86+
+"Path too deep"
87+
+
88+
+getpath([range(10000) | 0])
89+
+null
90+
+null
91+
+
92+
+try getpath([range(10001) | 0]) catch .
93+
+null
94+
+"Path too deep"
95+
+
96+
+delpaths([[range(10000) | 0]])
97+
+null
98+
+null
99+
+
100+
+try delpaths([[range(10001) | 0]]) catch .
101+
+null
102+
+"Path too deep"
103+
+
104+
builtins|any(.[:1] == "_")
105+
null
106+
false
107+
--
108+
2.45.4
109+

SPECS/jq/CVE-2026-33948.patch

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
From 6b00368687afbad32cb454a1cddb4af18ae2eb8b Mon Sep 17 00:00:00 2001
2+
From: itchyny <itchyny@cybozu.co.jp>
3+
Date: Mon, 13 Apr 2026 08:46:11 +0900
4+
Subject: [PATCH] Fix NUL truncation in the JSON parser
5+
6+
This fixes CVE-2026-33948.
7+
8+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
9+
Upstream-reference: https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b.patch
10+
---
11+
src/util.c | 8 +-------
12+
tests/shtest | 6 ++++++
13+
2 files changed, 7 insertions(+), 7 deletions(-)
14+
15+
diff --git a/src/util.c b/src/util.c
16+
index de44fa6..422a8b8 100644
17+
--- a/src/util.c
18+
+++ b/src/util.c
19+
@@ -311,13 +311,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) {
20+
if (p != NULL)
21+
state->current_line++;
22+
23+
- if (p == NULL && state->parser != NULL) {
24+
- /*
25+
- * There should be no NULs in JSON texts (but JSON text
26+
- * sequences are another story).
27+
- */
28+
- state->buf_valid_len = strlen(state->buf);
29+
- } else if (p == NULL && feof(state->current_input)) {
30+
+ if (p == NULL && feof(state->current_input)) {
31+
size_t i;
32+
33+
/*
34+
diff --git a/tests/shtest b/tests/shtest
35+
index a471889..0397ca0 100755
36+
--- a/tests/shtest
37+
+++ b/tests/shtest
38+
@@ -609,4 +609,10 @@ $VALGRIND $Q $JQ . <<\NUM
39+
-10E-1000000001
40+
NUM
41+
42+
+# CVE-2026-33948: No NUL truncation in the JSON parser
43+
+if printf '{}\x00{}' | $JQ >/dev/null 2> /dev/null; then
44+
+ printf 'Error expected but jq exited successfully\n' 1>&2
45+
+ exit 1
46+
+fi
47+
+
48+
exit 0
49+
--
50+
2.45.4
51+

SPECS/jq/CVE-2026-39956.patch

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
From e5c82a886d941ce534dfa1694b0d5e8f364a90fc Mon Sep 17 00:00:00 2001
2+
From: tlsbollei <170938166+tlsbollei@users.noreply.github.com>
3+
Date: Wed, 8 Apr 2026 21:43:46 +0200
4+
Subject: [PATCH] Add runtime type checks to f_string_indexes
5+
6+
This fixes CVE-2026-39956.
7+
8+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
9+
Upstream-reference: https://github.com/jqlang/jq/commit/fdf8ef0f0810e3d365cdd5160de43db46f57ed03.patch
10+
---
11+
src/builtin.c | 8 ++++++++
12+
1 file changed, 8 insertions(+)
13+
14+
diff --git a/src/builtin.c b/src/builtin.c
15+
index 902490d..3cb8eb7 100644
16+
--- a/src/builtin.c
17+
+++ b/src/builtin.c
18+
@@ -1212,6 +1212,14 @@ static jv f_string_explode(jq_state *jq, jv a) {
19+
}
20+
21+
static jv f_string_indexes(jq_state *jq, jv a, jv b) {
22+
+ if (jv_get_kind(a) != JV_KIND_STRING) {
23+
+ jv_free(b);
24+
+ return type_error(a, "cannot be searched, as it is not a string");
25+
+ }
26+
+ if (jv_get_kind(b) != JV_KIND_STRING) {
27+
+ jv_free(a);
28+
+ return type_error(b, "is not a string");
29+
+ }
30+
return jv_string_indexes(a, b);
31+
}
32+
33+
--
34+
2.45.4
35+

SPECS/jq/CVE-2026-39979.patch

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
From 504ac57fa642aebc7d8652bade76eb1fc578a020 Mon Sep 17 00:00:00 2001
2+
From: itchyny <itchyny@cybozu.co.jp>
3+
Date: Mon, 13 Apr 2026 11:04:52 +0900
4+
Subject: [PATCH] Fix out-of-bounds read in jv_parse_sized()
5+
6+
This fixes CVE-2026-39979.
7+
8+
Co-authored-by: Mattias Wadman <mattias.wadman@gmail.com>
9+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
10+
Upstream-reference: https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f.patch
11+
---
12+
src/jv_parse.c | 3 ++-
13+
1 file changed, 2 insertions(+), 1 deletion(-)
14+
15+
diff --git a/src/jv_parse.c b/src/jv_parse.c
16+
index 9755b8a..84a847f 100644
17+
--- a/src/jv_parse.c
18+
+++ b/src/jv_parse.c
19+
@@ -890,8 +890,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) {
20+
21+
if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
22+
jv msg = jv_invalid_get_msg(value);
23+
- value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')",
24+
+ value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')",
25+
jv_string_value(msg),
26+
+ length,
27+
string));
28+
jv_free(msg);
29+
}
30+
--
31+
2.45.4
32+

SPECS/jq/CVE-2026-40164.patch

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
From 08d00ef18394e7cf5b1860224af821c6000e5858 Mon Sep 17 00:00:00 2001
2+
From: itchyny <itchyny@cybozu.co.jp>
3+
Date: Mon, 13 Apr 2026 08:53:26 +0900
4+
Subject: [PATCH] Randomize hash seed to mitigate hash collision DoS attacks
5+
6+
The hash function used a fixed seed, allowing attackers to craft colliding keys
7+
and cause O(n^2) object parsing performance. Initialize the seed from a random
8+
source at process startup to prevent the attack. This fixes CVE-2026-40164.
9+
10+
Co-authored-by: Asaf Meizner <asafmeizner@gmail.com>
11+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
12+
Upstream-reference: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784.patch
13+
---
14+
configure.ac | 2 ++
15+
src/jv.c | 34 ++++++++++++++++++++++++++++++++--
16+
2 files changed, 34 insertions(+), 2 deletions(-)
17+
18+
diff --git a/configure.ac b/configure.ac
19+
index 118e084..7f12b52 100644
20+
--- a/configure.ac
21+
+++ b/configure.ac
22+
@@ -149,6 +149,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define
23+
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])],
24+
[], [[#include <time.h>]])
25+
AC_FIND_FUNC([setlocale], [c], [#include <locale.h>], [0,0])
26+
+AC_FIND_FUNC([arc4random], [c], [#include <stdlib.h>], [])
27+
+AC_FIND_FUNC([getentropy], [c], [#include <unistd.h>], [0, 0])
28+
29+
dnl Figure out if we have the pthread functions we actually need
30+
AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include <pthread.h>], [NULL, NULL])
31+
diff --git a/src/jv.c b/src/jv.c
32+
index 73387d8..08ded35 100644
33+
--- a/src/jv.c
34+
+++ b/src/jv.c
35+
@@ -40,6 +40,10 @@
36+
#include <limits.h>
37+
#include <math.h>
38+
#include <float.h>
39+
+#include <time.h>
40+
+#include <unistd.h>
41+
+#include <fcntl.h>
42+
+#include <pthread.h>
43+
44+
#include "jv_alloc.h"
45+
#include "jv.h"
46+
@@ -1183,7 +1187,33 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) {
47+
}
48+
}
49+
50+
-static const uint32_t HASH_SEED = 0x432A9843;
51+
+static uint32_t hash_seed;
52+
+static pthread_once_t hash_seed_once = PTHREAD_ONCE_INIT;
53+
+
54+
+static void jvp_hash_seed_init(void) {
55+
+ uint32_t seed;
56+
+#if defined(HAVE_ARC4RANDOM)
57+
+ seed = arc4random();
58+
+#elif defined(HAVE_GETENTROPY)
59+
+ if (getentropy(&seed, sizeof(seed)) != 0)
60+
+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL);
61+
+#else
62+
+ int fd = open("/dev/urandom", O_RDONLY);
63+
+ if (fd >= 0) {
64+
+ if (read(fd, &seed, sizeof(seed)) != 4)
65+
+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL);
66+
+ close(fd);
67+
+ } else {
68+
+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL);
69+
+ }
70+
+#endif
71+
+ hash_seed = seed;
72+
+}
73+
+
74+
+static uint32_t jvp_hash_seed(void) {
75+
+ pthread_once(&hash_seed_once, jvp_hash_seed_init);
76+
+ return hash_seed;
77+
+}
78+
79+
static uint32_t rotl32 (uint32_t x, int8_t r){
80+
return (x << r) | (x >> (32 - r));
81+
@@ -1202,7 +1232,7 @@ static uint32_t jvp_string_hash(jv jstr) {
82+
int len = (int)jvp_string_length(str);
83+
const int nblocks = len / 4;
84+
85+
- uint32_t h1 = HASH_SEED;
86+
+ uint32_t h1 = jvp_hash_seed();
87+
88+
const uint32_t c1 = 0xcc9e2d51;
89+
const uint32_t c2 = 0x1b873593;
90+
--
91+
2.45.4
92+

0 commit comments

Comments
 (0)