mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-07-27 07:57:27 +00:00
jq: Fix CVE-2026-44777
The upstream fix [3] is for a newer jq codebase. Debian has already backported this fix in jq 1.8.1-7. Use the Debian patch [1], which fixes this CVE as tracked in Debian bug #1136445 [2]. [1] https://sources.debian.org/src/jq/1.8.1-7/debian/patches/CVE-2026-44777.patch [2] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1136445 [3] https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c Signed-off-by: Shubham Pushpkar <spushpka@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
committed by
Anuj Mittal
parent
22b6fca850
commit
532ac37990
@@ -0,0 +1,243 @@
|
|||||||
|
From f58787c41835d9b17795730cb04925fdba25c71c Mon Sep 17 00:00:00 2001
|
||||||
|
From: itchyny <itchyny@cybozu.co.jp>
|
||||||
|
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.
|
||||||
|
|
||||||
|
CVE: CVE-2026-44777
|
||||||
|
Upstream-Status: Backport [https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c]
|
||||||
|
|
||||||
|
Backport Changes:
|
||||||
|
- The upstream src/linker.c patch deletes an `if (nerrors == 0)` wrapper
|
||||||
|
around the old library registration block. Wrynose jq 1.8.1 does not
|
||||||
|
have that wrapper in the same form, so this backport has two fewer
|
||||||
|
deleted lines in src/linker.c while preserving the upstream
|
||||||
|
circular-import detection logic.
|
||||||
|
|
||||||
|
(cherry picked from commit f58787c41835d9b17795730cb04925fdba25c71c)
|
||||||
|
Signed-off-by: Shubham Pushpkar <spushpka@cisco.com>
|
||||||
|
---
|
||||||
|
Makefile.am | 2 ++
|
||||||
|
src/linker.c | 57 +++++++++++++++++++++++++------------
|
||||||
|
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(+), 18 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
|
||||||
|
|
||||||
|
diff --git a/Makefile.am b/Makefile.am
|
||||||
|
index 76e35df..6fcc013 100644
|
||||||
|
--- a/Makefile.am
|
||||||
|
+++ b/Makefile.am
|
||||||
|
@@ -223,6 +223,8 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
|
||||||
|
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 \
|
||||||
|
diff --git a/src/linker.c b/src/linker.c
|
||||||
|
index e7d1024..0c9c34b 100644
|
||||||
|
--- a/src/linker.c
|
||||||
|
+++ b/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,
|
||||||
|
@@ -299,14 +303,24 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
|
||||||
|
} 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);
|
||||||
|
@@ -348,30 +362,38 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, int opt
|
||||||
|
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);
|
||||||
|
@@ -409,7 +431,7 @@ jv load_module_meta(jq_state *jq, jv mod_relpath) {
|
||||||
|
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;
|
||||||
|
@@ -433,14 +455,13 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) {
|
||||||
|
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
|
||||||
|
diff --git a/tests/modules/cycle_a.jq b/tests/modules/cycle_a.jq
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..30c1dea
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/modules/cycle_a.jq
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+import "cycle_b" as b;
|
||||||
|
+def f: null;
|
||||||
|
diff --git a/tests/modules/cycle_b.jq b/tests/modules/cycle_b.jq
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..3fdc360
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/modules/cycle_b.jq
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+import "cycle_a" as a;
|
||||||
|
+def f: null;
|
||||||
|
diff --git a/tests/modules/cycle_self.jq b/tests/modules/cycle_self.jq
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..8365eab
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/modules/cycle_self.jq
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+import "cycle_self" as s;
|
||||||
|
+def f: null;
|
||||||
|
diff --git a/tests/shtest b/tests/shtest
|
||||||
|
index 505d45d..f5794fb 100755
|
||||||
|
--- a/tests/shtest
|
||||||
|
+++ b/tests/shtest
|
||||||
|
@@ -369,17 +369,40 @@ if ! HOME="$mods/home2" $VALGRIND $Q $JQ -n 'include "g"; empty'; then
|
||||||
|
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
|
||||||
|
|
||||||
|
--
|
||||||
|
2.35.6
|
||||||
@@ -23,6 +23,7 @@ SRC_URI = "git://github.com/jqlang/jq.git;protocol=https;branch=master;tag=jq-${
|
|||||||
file://CVE-2026-41257.patch \
|
file://CVE-2026-41257.patch \
|
||||||
file://CVE-2026-43894.patch \
|
file://CVE-2026-43894.patch \
|
||||||
file://CVE-2026-43896.patch \
|
file://CVE-2026-43896.patch \
|
||||||
|
file://CVE-2026-44777.patch \
|
||||||
"
|
"
|
||||||
|
|
||||||
inherit autotools ptest
|
inherit autotools ptest
|
||||||
|
|||||||
Reference in New Issue
Block a user