From: Anton Skorup <antonsk@axis.com>
To: <openembedded-devel@lists.openembedded.org>
Cc: Anton Skorup <anton@skorup.se>, Anton Skorup <anton.skorup@axis.com>
Subject: [PATCH 3/8] jq: patch CVE-2026-44777
Date: Tue, 16 Jun 2026 08:27:49 +0200 [thread overview]
Message-ID: <20260616062754.748436-3-antonsk@axis.com> (raw)
In-Reply-To: <20260616062754.748436-1-antonsk@axis.com>
From: Anton Skorup <anton@skorup.se>
CVE details: https://www.cve.org/CVERecord?id=CVE-2026-44777
Signed-off-by: Anton Skorup <anton.skorup@axis.com>
---
.../jq/jq/CVE-2026-44777.patch | 233 ++++++++++++++++++
meta-oe/recipes-devtools/jq/jq_1.8.1.bb | 1 +
2 files changed, 234 insertions(+)
create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2026-44777.patch
diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2026-44777.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2026-44777.patch
new file mode 100644
index 0000000000..f6bf926a0a
--- /dev/null
+++ b/meta-oe/recipes-devtools/jq/jq/CVE-2026-44777.patch
@@ -0,0 +1,233 @@
+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.
+
+Signed-off-by: Anton Skorup <anton.skorup@axis.com>
+Upstream-Status: Backport [https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c]
+---
+ Makefile.am | 2 ++
+ src/linker.c | 59 ++++++++++++++++++++++++-------------
+ 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(+), 20 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 acb94435f4..e2321bb196 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -232,6 +232,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 e9027004cc..03f46db05c 100644
+--- a/src/linker.c
++++ b/src/linker.c
+@@ -20,9 +20,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,
+@@ -303,14 +307,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);
+@@ -352,32 +366,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;
+ }
+ }
+- if (nerrors == 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);
+@@ -415,7 +435,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;
+@@ -441,14 +461,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 0000000000..30c1deaedf
+--- /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 0000000000..3fdc360fcd
+--- /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 0000000000..8365eab1a4
+--- /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 fa972de870..aca82790bc 100755
+--- a/tests/shtest
++++ b/tests/shtest
+@@ -382,17 +382,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
+
diff --git a/meta-oe/recipes-devtools/jq/jq_1.8.1.bb b/meta-oe/recipes-devtools/jq/jq_1.8.1.bb
index 34616e0af6..2e6c3a3eea 100644
--- a/meta-oe/recipes-devtools/jq/jq_1.8.1.bb
+++ b/meta-oe/recipes-devtools/jq/jq_1.8.1.bb
@@ -18,6 +18,7 @@ SRC_URI = "git://github.com/jqlang/jq.git;protocol=https;branch=master;tag=jq-${
file://CVE-2026-33948.patch \
file://CVE-2026-39979.patch \
file://CVE-2026-41256.patch \
+ file://CVE-2026-44777.patch \
file://CVE-2026-49389.patch \
"
--
2.43.0
next prev parent reply other threads:[~2026-06-16 7:12 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-16 6:27 [PATCHv2 1/8] jq: patch CVE-2026-49839 Anton Skorup
2026-06-16 6:27 ` [PATCH 2/8] jq: patch CVE-2026-41256 Anton Skorup
2026-06-16 6:27 ` Anton Skorup [this message]
2026-06-16 6:27 ` [PATCH 4/8] jq: patch CVE-2026-43896 Anton Skorup
2026-06-16 6:27 ` [PATCH 5/8] jq: patch CVE-2026-41257 Anton Skorup
2026-06-16 6:27 ` [PATCH 6/8] jq: patch CVE-2026-40612 Anton Skorup
2026-06-16 6:27 ` [PATCH 7/8] jq: patch CVE-2026-43894 Anton Skorup
2026-06-16 6:27 ` [PATCH 8/8] jq: patch CVE-2026-43895 Anton Skorup
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260616062754.748436-3-antonsk@axis.com \
--to=antonsk@axis.com \
--cc=anton.skorup@axis.com \
--cc=anton@skorup.se \
--cc=openembedded-devel@lists.openembedded.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.