Skip to content
Draft
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
141 changes: 141 additions & 0 deletions SPECS/jq/CVE-2026-40612.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
From d1a12569d91641135976a8536776a4a329c02cc2 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
Date: Fri, 24 Apr 2026 22:02:24 +0900
Subject: [PATCH] Limit the containment check depth

This fixes CVE-2026-40612.

Modified to apply to Azure Linux.
Added extra closed brackets `()` after try in jq.test file to avoid syntax errors caused due to version differences.

Upstream Patch reference: https://github.com/jqlang/jq/commit/d1a12569d91641135976a8536776a4a329c02cc2.patch
---
src/builtin.c | 5 ++++-
src/jv.c | 40 +++++++++++++++++++++++++++-------------
tests/jq.test | 9 +++++++++
3 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/src/builtin.c b/src/builtin.c
index 3cb8eb7..08b94ac 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -471,7 +471,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");
}
diff --git a/src/jv.c b/src/jv.c
index 08ded35..5a2c3a2 100644
--- a/src/jv.c
+++ b/src/jv.c
@@ -914,19 +914,19 @@ static void jvp_clamp_slice_params(int len, int *pstart, int *pend)
}


-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;
}
}
@@ -1794,7 +1794,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;
@@ -1802,9 +1802,9 @@ static int jvp_object_contains(jv a, jv b) {
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;
}
@@ -2035,14 +2035,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) {
@@ -2058,3 +2067,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);
+}
diff --git a/tests/jq.test b/tests/jq.test
index 758a161..0516ff1 100644
--- a/tests/jq.test
+++ b/tests/jq.test
@@ -2155,3 +2155,12 @@ try ltrimstr("x") catch "x", try rtrimstr("x") catch "x" | "ok"
{"hey":[]}
"ok"
"ok"
+
+# 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"
--
2.43.0

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

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/5a015deae35d19e3ebbc65db6c157a80e76df738.patch
---
src/main.c | 8 ++++++++
tests/shtest | 7 +++++++
2 files changed, 15 insertions(+)

diff --git a/src/main.c b/src/main.c
index 43586c4..f462e4d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -677,6 +677,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));
diff --git a/tests/shtest b/tests/shtest
index 0397ca0..505d45d 100755
--- a/tests/shtest
+++ b/tests/shtest
@@ -615,4 +615,11 @@ if printf '{}\x00{}' | $JQ >/dev/null 2> /dev/null; then
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
--
2.45.4

55 changes: 55 additions & 0 deletions SPECS/jq/CVE-2026-41257.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
From 59ff1987e1d364e418984eb46943a53c3e7606b5 Mon Sep 17 00:00:00 2001
From: itchyny <itchyny@cybozu.co.jp>
Date: Fri, 24 Apr 2026 22:09:44 +0900
Subject: [PATCH] Fix signed-int overflow in `stack_reallocate`

This fixes CVE-2026-41257.

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/jqlang/jq/commit/01b3cded76daacbfddb7f8763700b0803bcb5c6f.patch
---
src/exec_stack.h | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/exec_stack.h b/src/exec_stack.h
index 2a063e8..159c56e 100644
--- a/src/exec_stack.h
+++ b/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(struct stack* s, stack_ptr p) {
}

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) {
--
2.45.4

Loading
Loading