Openembedded Devel Discussions
 help / color / mirror / Atom feed
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: [meta-oe][PATCHv2 3/8] jq: patch CVE-2026-44777
Date: Wed, 17 Jun 2026 07:30:35 +0200	[thread overview]
Message-ID: <20260617053040.990143-3-antonsk@axis.com> (raw)
In-Reply-To: <20260617053040.990143-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>
---
v2
 * Rebased on master-next
---
 .../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 2092fe962a..2634fd52a2 100644
--- a/meta-oe/recipes-devtools/jq/jq_1.8.1.bb
+++ b/meta-oe/recipes-devtools/jq/jq_1.8.1.bb
@@ -19,6 +19,7 @@ SRC_URI = "git://github.com/jqlang/jq.git;protocol=https;branch=master;tag=jq-${
            file://CVE-2026-39979.patch \
            file://CVE-2026-41256.patch \
            file://CVE-2026-47770.patch \
+           file://CVE-2026-44777.patch \
            file://CVE-2026-49389.patch \
            file://CVE-2026-49839.patch \
            "
-- 
2.43.0



  parent reply	other threads:[~2026-06-17  6:13 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-17  5:30 [meta-oe][PATCHv3 1/8] jq: patch CVE-2026-49839 Anton Skorup
2026-06-17  5:30 ` [meta-oe][PATCHv2 2/8] jq: patch CVE-2026-41256 Anton Skorup
2026-06-17  5:30 ` Anton Skorup [this message]
2026-06-17  5:30 ` [meta-oe][PATCHv2 4/8] jq: patch CVE-2026-43896 Anton Skorup
2026-06-17  5:30 ` [meta-oe][PATCHv2 5/8] jq: patch CVE-2026-41257 Anton Skorup
2026-06-17  5:30 ` [meta-oe][PATCHv2 6/8] jq: patch CVE-2026-40612 Anton Skorup
2026-06-17  5:30 ` [meta-oe][PATCHv2 7/8] jq: patch CVE-2026-43894 Anton Skorup
2026-06-17  5:30 ` [meta-oe][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=20260617053040.990143-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox