[meta-OE][scarthgap][PATCH 1/4] jq: Fix CVE-2026-43895
"Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]> Tue, 21 Jul 2026 02:39:35 -0700
| Newsgroups | org.openembedded.lists.openembedded-devel |
|---|---|
| Message-ID | <[email protected]> |
From: Darsh Kelaiya <[email protected]> This patch applies the upstream fix as referenced in [2], using the commit shown in [1]. [1] https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361 [2] https://github.com/jqlang/jq/security/advisories/GHSA-7q7g-mrq3-phxr Signed-off-by: Darsh Kelaiya <[email protected]> --- .../jq/jq/CVE-2026-43895.patch | 206 ++++++++++++++++++ meta-oe/recipes-devtools/jq/jq_1.7.1.bb | 1 + 2 files changed, 207 insertions(+) create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2026-43895.patch diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2026-43895.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2026-43895.patch new file mode 100644 index 0000000000..efde45c710 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2026-43895.patch @@ -0,0 +1,206 @@ +From 5192a59c2c6ca6ab0667cbee608db29c0710f346 Mon Sep 17 00:00:00 2001 +From: itchyny <[email protected]> +Date: Sat, 9 May 2026 17:08:43 +0900 +Subject: [PATCH 1/2] Reject embedded NUL bytes in module import paths + +jq accepts embedded NUL bytes at the language level but resolves +module import paths through NUL-terminated C strings, so the path +validated by policy or audit code could differ from the on-disk +path jq actually opens. Pass jv through gen_import so the AST +preserves the original bytes, and reject embedded NULs in +validate_relpath. + +Fixes CVE-2026-43895. + +CVE: CVE-2026-43895 +Upstream-Status: Backport [https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361] + +Backport Changes: +- Kept jq 1.7.1 HOME lookup in linker.c. + The newer get_home() flow is absent from the target. + Only the jv-based gen_import() call was required. +- Limited parser.c to three import/include action changes. + The newer grammar caused unrelated generated-parser churn. + Keeping target numbering avoids unrelated generated changes. + +(cherry picked from commit 9d223f153c3632a207fa071caaa6292da33ae361) +Signed-off-by: Darsh Kelaiya <[email protected]> +--- + src/compile.c | 12 ++++++++---- + src/compile.h | 2 +- + src/linker.c | 6 +++++- + src/parser.c | 16 +++------------- + src/parser.y | 16 +++------------- + tests/shtest | 17 +++++++++++++++++ + 6 files changed, 37 insertions(+), 32 deletions(-) + +diff --git a/src/compile.c b/src/compile.c +index e5e65f2..27b2cfd 100644 +--- a/src/compile.c ++++ b/src/compile.c +@@ -526,13 +526,17 @@ jv block_module_meta(block b) { + return jv_null(); + } + +-block gen_import(const char* name, const char* as, int is_data) { ++block gen_import(jv name, jv as, int is_data) { ++ assert(jv_get_kind(name) == JV_KIND_STRING); ++ assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING); + inst* i = inst_new(DEPS); + jv meta = jv_object(); +- if (as != NULL) +- meta = jv_object_set(meta, jv_string("as"), jv_string(as)); ++ if (jv_is_valid(as)) ++ meta = jv_object_set(meta, jv_string("as"), as); ++ else ++ jv_free(as); + meta = jv_object_set(meta, jv_string("is_data"), is_data ? jv_true() : jv_false()); +- meta = jv_object_set(meta, jv_string("relpath"), jv_string(name)); ++ meta = jv_object_set(meta, jv_string("relpath"), name); + i->imm.constant = meta; + return inst_block(i); + } +diff --git a/src/compile.h b/src/compile.h +index c1512e6..bac65ef 100644 +--- a/src/compile.h ++++ b/src/compile.h +@@ -33,7 +33,7 @@ block gen_op_pushk_under(jv constant); + + block gen_module(block metadata); + jv block_module_meta(block b); +-block gen_import(const char* name, const char *as, int is_data); ++block gen_import(jv name, jv as, int is_data); + block gen_import_meta(block import, block metadata); + block gen_function(const char* name, block formals, block body); + block gen_param_regular(const char* name); +diff --git a/src/linker.c b/src/linker.c +index e7d1024..4b15008 100644 +--- a/src/linker.c ++++ b/src/linker.c +@@ -93,6 +93,10 @@ static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv + // in between). + static jv validate_relpath(jv name) { + const char *s = jv_string_value(name); ++ if (strlen(s) != (size_t)jv_string_length_bytes(jv_copy(name))) { ++ jv_free(name); ++ return jv_invalid_with_msg(jv_string("Module path contains a NUL byte")); ++ } + if (strchr(s, '\\')) { + jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s)); + jv_free(name); +@@ -423,7 +427,7 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) { + char* home = getenv("HOME"); + if (home) { // silently ignore no $HOME + /* Import ~/.jq as a library named "" found in $HOME */ +- block import = gen_import_meta(gen_import("", NULL, 0), ++ block import = gen_import_meta(gen_import(jv_string(""), jv_invalid(), 0), + gen_const(JV_OBJECT( + jv_string("optional"), jv_true(), + jv_string("search"), jv_string(home)))); +diff --git a/src/parser.c b/src/parser.c +index 0599db7..c50f2fb 100644 +--- a/src/parser.c ++++ b/src/parser.c +@@ -3081,13 +3081,8 @@ yyreduce: + case 50: /* ImportWhat: "import" ImportFrom "as" BINDING */ + #line 506 "src/parser.y" + { +- jv v = block_const((yyvsp[-2].blk)); +- // XXX Make gen_import take only blocks and the int is_data so we +- // don't have to free so much stuff here +- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 1); ++ (yyval.blk) = gen_import(block_const((yyvsp[-2].blk)), (yyvsp[0].literal), 1); + block_free((yyvsp[-2].blk)); +- jv_free((yyvsp[0].literal)); +- jv_free(v); + } + #line 3093 "src/parser.c" + break; +@@ -3095,11 +3090,8 @@ yyreduce: + case 51: /* ImportWhat: "import" ImportFrom "as" IDENT */ + #line 515 "src/parser.y" + { +- jv v = block_const((yyvsp[-2].blk)); +- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 0); ++ (yyval.blk) = gen_import(block_const((yyvsp[-2].blk)), (yyvsp[0].literal), 0); + block_free((yyvsp[-2].blk)); +- jv_free((yyvsp[0].literal)); +- jv_free(v); + } + #line 3105 "src/parser.c" + break; +@@ -3107,10 +3099,8 @@ yyreduce: + case 52: /* ImportWhat: "include" ImportFrom */ + #line 522 "src/parser.y" + { +- jv v = block_const((yyvsp[0].blk)); +- (yyval.blk) = gen_import(jv_string_value(v), NULL, 0); ++ (yyval.blk) = gen_import(block_const((yyvsp[0].blk)), jv_invalid(), 0); + block_free((yyvsp[0].blk)); +- jv_free(v); + } + #line 3116 "src/parser.c" + break; +diff --git a/src/parser.y b/src/parser.y +index 3d24689..2901cab 100644 +--- a/src/parser.y ++++ b/src/parser.y +@@ -504,26 +504,16 @@ ImportWhat Exp ';' { + + ImportWhat: + "import" ImportFrom "as" BINDING { +- jv v = block_const($2); +- // XXX Make gen_import take only blocks and the int is_data so we +- // don't have to free so much stuff here +- $$ = gen_import(jv_string_value(v), jv_string_value($4), 1); ++ $$ = gen_import(block_const($2), $4, 1); + block_free($2); +- jv_free($4); +- jv_free(v); + } | + "import" ImportFrom "as" IDENT { +- jv v = block_const($2); +- $$ = gen_import(jv_string_value(v), jv_string_value($4), 0); ++ $$ = gen_import(block_const($2), $4, 0); + block_free($2); +- jv_free($4); +- jv_free(v); + } | + "include" ImportFrom { +- jv v = block_const($2); +- $$ = gen_import(jv_string_value(v), NULL, 0); ++ $$ = gen_import(block_const($2), jv_invalid(), 0); + block_free($2); +- jv_free(v); + } + + ImportFrom: +diff --git a/tests/shtest b/tests/shtest +index 505d45d..4a5978c 100755 +--- a/tests/shtest ++++ b/tests/shtest +@@ -622,4 +622,21 @@ if echo '42' | $JQ -f "$d/nul_prog.jq" >/dev/null 2>/dev/null; then + exit 1 + fi + ++# CVE-2026-43895: No NUL bytes in module/data import paths ++printf 'import "a\\u0000b" as $x; .' > "$d/nul_import.jq" ++if $JQ -nf "$d/nul_import.jq" >/dev/null 2>/dev/null; then ++ printf 'Error expected for import path with NUL bytes\n' 1>&2 ++ exit 1 ++fi ++printf 'include "a\\u0000b"; .' > "$d/nul_include.jq" ++if $JQ -nf "$d/nul_include.jq" >/dev/null 2>/dev/null; then ++ printf 'Error expected for include path with NUL bytes\n' 1>&2 ++ exit 1 ++fi ++printf '"a\\u0000b" | modulemeta' > "$d/nul_modulemeta.jq" ++if $JQ -nf "$d/nul_modulemeta.jq" >/dev/null 2>/dev/null; then ++ printf 'Error expected for modulemeta with NUL bytes\n' 1>&2 ++ exit 1 ++fi ++ + exit 0 +-- +2.44.4 + diff --git a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb index 2fc47ef92c..2f08f583cc 100644 --- a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb +++ b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb @@ -25,6 +25,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/${BPN}-${PV}/${BPN}-${PV}.tar.gz \ file://CVE-2026-41257.patch \ file://CVE-2026-43894.patch \ file://CVE-2026-43896.patch \ + file://CVE-2026-43895.patch \ " SRC_URI[sha256sum] = "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2" -- 2.35.6