* [Buildroot] [PATCH 1/2] package/jq: patch various CVEs @ 2026-06-30 20:15 Thomas Perale via buildroot 2026-06-30 20:15 ` [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 Thomas Perale via buildroot 0 siblings, 1 reply; 4+ messages in thread From: Thomas Perale via buildroot @ 2026-06-30 20:15 UTC (permalink / raw) To: buildroot; +Cc: Angelo Compagnucci, Danomi Manchego Fixes the following vulnerabilities by importing upstream patches: - CVE-2026-39979: https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f - CVE-2026-33948: https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b - CVE-2026-33947: https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f - CVE-2026-32316: https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5 - CVE-2026-40164: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784 - CVE-2026-40612: https://github.com/jqlang/jq/commit/d1a12569d91641135976a8536776a4a329c02cc2 - CVE-2026-41256: https://github.com/jqlang/jq/commit/5a015deae35d19e3ebbc65db6c157a80e76df738 - CVE-2026-41257: https://github.com/jqlang/jq/commit/01b3cded76daacbfddb7f8763700b0803bcb5c6f - CVE-2026-43894: https://github.com/jqlang/jq/commit/9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 - CVE-2026-43895: https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361 - CVE-2026-43896: https://github.com/jqlang/jq/commit/532ccea6080ed6758f39fe9f6208a44b665023d2 - CVE-2026-44777: https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c - CVE-2026-47770: https://github.com/jqlang/jq/commit/7122866869960b55cea3646bc91334ef55787831 - CVE-2026-49839: https://github.com/jqlang/jq/commit/e987df0d463d85fd70825e042a082427e8275b86 - CVE-2026-54679: https://github.com/jqlang/jq/commit/46d1da30944ce93dd671ac72b6513fc0eb747837 Tests were stripped out of the patches. Signed-off-by: Thomas Perale <thomas.perale@mind.be> --- ...out-of-bounds-read-in-jv-parse-sized.patch | 30 + ...ix-NUL-truncation-in-the-JSON-parser.patch | 35 + ...path-depth-to-prevent-stack-overflow.patch | 72 + ...pend-and-jvp-string-copy-replace-bad.patch | 53 + ...-mitigate-hash-collision-DoS-attacks.patch | 90 + ...06-limit-the-containment-check-depth.patch | 120 ++ ...ation-in-program-files-loaded-with-f.patch | 34 + ...ned-int-overflow-in-stack-reallocate.patch | 53 + ...-literals-longer-than-DEC-MAX-DIGITS.patch | 53 + ...ded-NUL-bytes-in-module-import-paths.patch | 1512 +++++++++++++++++ ...erge-depth-to-prevent-stack-overflow.patch | 66 + ...le-imports-to-prevent-stack-overflow.patch | 142 ++ ...al-equality-and-comparison-recursion.patch | 423 +++++ ...-buffer-overflow-in-raw-file-loading.patch | 32 + ...-and-propagate-invalid-jv-in-implode.patch | 73 + package/jq/jq.mk | 45 + 16 files changed, 2833 insertions(+) create mode 100644 package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch create mode 100644 package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch create mode 100644 package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch create mode 100644 package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch create mode 100644 package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch create mode 100644 package/jq/0006-limit-the-containment-check-depth.patch create mode 100644 package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch create mode 100644 package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch create mode 100644 package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch create mode 100644 package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch create mode 100644 package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch create mode 100644 package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch create mode 100644 package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch create mode 100644 package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch create mode 100644 package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch diff --git a/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch b/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch new file mode 100644 index 0000000000..77fe358e7e --- /dev/null +++ b/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch @@ -0,0 +1,30 @@ +From 2f09060afab23fe9390cce7cb860b10416e1bf5f Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Mon, 13 Apr 2026 11:04:52 +0900 +Subject: [PATCH] Fix out-of-bounds read in jv_parse_sized() + +This fixes CVE-2026-39979. + +Co-authored-by: Mattias Wadman <mattias.wadman@gmail.com> +Upstream: https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f +CVE: CVE-2026-39979 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv_parse.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/jv_parse.c b/src/jv_parse.c +index aa2054cc09..56847b5eaa 100644 +--- a/src/jv_parse.c ++++ b/src/jv_parse.c +@@ -892,8 +892,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) { + + if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) { + jv msg = jv_invalid_get_msg(value); +- value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", ++ value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')", + jv_string_value(msg), ++ length, + string)); + jv_free(msg); + } diff --git a/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch b/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch new file mode 100644 index 0000000000..90d26fbd34 --- /dev/null +++ b/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch @@ -0,0 +1,35 @@ +From 6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Mon, 13 Apr 2026 08:46:11 +0900 +Subject: [PATCH] Fix NUL truncation in the JSON parser + +This fixes CVE-2026-33948. + +Upstream: https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b +CVE: CVE-2026-33948 +[thomas: remove tests] +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/util.c | 8 +------- + tests/shtest | 6 ++++++ + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/src/util.c b/src/util.c +index fdfdb96d88..80d65fc808 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -309,13 +309,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) { + if (p != NULL) + state->current_line++; + +- if (p == NULL && state->parser != NULL) { +- /* +- * There should be no NULs in JSON texts (but JSON text +- * sequences are another story). +- */ +- state->buf_valid_len = strlen(state->buf); +- } else if (p == NULL && feof(state->current_input)) { ++ if (p == NULL && feof(state->current_input)) { + size_t i; + + /* diff --git a/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch b/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch new file mode 100644 index 0000000000..498d4b81f6 --- /dev/null +++ b/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch @@ -0,0 +1,72 @@ +From fb59f1491058d58bdc3e8dd28f1773d1ac690a1f Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Mon, 13 Apr 2026 11:23:40 +0900 +Subject: [PATCH] Limit path depth to prevent stack overflow + +Deeply nested path arrays can cause unbounded recursion in +`jv_setpath`, `jv_getpath`, and `jv_delpaths`, leading to +stack overflow. Add a depth limit of 10000 to match the +existing `tojson` depth limit. This fixes CVE-2026-33947. + +CVE: CVE-2026-33947 +Upstream: https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f +[thomas: remove tests] +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv_aux.c | 21 +++++++++++++++++++++ + tests/jq.test | 25 +++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/src/jv_aux.c b/src/jv_aux.c +index 018f380b10..fd5ff96684 100644 +--- a/src/jv_aux.c ++++ b/src/jv_aux.c +@@ -375,6 +375,10 @@ static jv jv_dels(jv t, jv keys) { + return t; + } + ++#ifndef MAX_PATH_DEPTH ++#define MAX_PATH_DEPTH (10000) ++#endif ++ + jv jv_setpath(jv root, jv path, jv value) { + if (jv_get_kind(path) != JV_KIND_ARRAY) { + jv_free(value); +@@ -382,6 +386,12 @@ jv jv_setpath(jv root, jv path, jv value) { + jv_free(path); + return jv_invalid_with_msg(jv_string("Path must be specified as an array")); + } ++ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { ++ jv_free(value); ++ jv_free(root); ++ jv_free(path); ++ return jv_invalid_with_msg(jv_string("Path too deep")); ++ } + if (!jv_is_valid(root)){ + jv_free(value); + jv_free(path); +@@ -434,6 +444,11 @@ jv jv_getpath(jv root, jv path) { + jv_free(path); + return jv_invalid_with_msg(jv_string("Path must be specified as an array")); + } ++ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { ++ jv_free(root); ++ jv_free(path); ++ return jv_invalid_with_msg(jv_string("Path too deep")); ++ } + if (!jv_is_valid(root)) { + jv_free(path); + return root; +@@ -511,6 +526,12 @@ jv jv_delpaths(jv object, jv paths) { + jv_free(elem); + return err; + } ++ if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) { ++ jv_free(object); ++ jv_free(paths); ++ jv_free(elem); ++ return jv_invalid_with_msg(jv_string("Path too deep")); ++ } + jv_free(elem); + } + if (jv_array_length(jv_copy(paths)) == 0) { diff --git a/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch b/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch new file mode 100644 index 0000000000..b835c9d1c8 --- /dev/null +++ b/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch @@ -0,0 +1,53 @@ +From e47e56d226519635768e6aab2f38f0ab037c09e5 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Thu, 12 Mar 2026 20:28:43 +0900 +Subject: [PATCH] Fix heap buffer overflow in `jvp_string_append` and + `jvp_string_copy_replace_bad` + +In `jvp_string_append`, the allocation size `(currlen + len) * 2` could +overflow `uint32_t` when `currlen + len` exceeds `INT_MAX`, causing a small +allocation followed by a large `memcpy`. + +In `jvp_string_copy_replace_bad`, the output buffer size calculation +`length * 3 + 1` could overflow `uint32_t`, again resulting in a small +allocation followed by a large write. + +Add overflow checks to both functions to return an error for strings +that would exceed `INT_MAX` in length. Fixes CVE-2026-32316. + +CVE: CVE-2026-32316 +Upstream: https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/jv.c b/src/jv.c +index 722a539391..2a62b48419 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { + const char* end = data + length; + const char* i = data; + +- uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD ++ // worst case: all bad bytes, each becomes a 3-byte U+FFFD ++ uint64_t maxlength = (uint64_t)length * 3 + 1; ++ if (maxlength >= INT_MAX) { ++ return jv_invalid_with_msg(jv_string("String too long")); ++ } ++ + jvp_string* s = jvp_string_alloc(maxlength); + char* out = s->data; + int c = 0; +@@ -1174,6 +1179,10 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) { + static jv jvp_string_append(jv string, const char* data, uint32_t len) { + jvp_string* s = jvp_string_ptr(string); + uint32_t currlen = jvp_string_length(s); ++ if ((uint64_t)currlen + len >= INT_MAX) { ++ jv_free(string); ++ return jv_invalid_with_msg(jv_string("String too long")); ++ } + + if (jvp_refcnt_unshared(string.u.ptr) && + jvp_string_remaining_space(s) >= len) { diff --git a/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch b/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch new file mode 100644 index 0000000000..ac4d6c445e --- /dev/null +++ b/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch @@ -0,0 +1,90 @@ +From 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Mon, 13 Apr 2026 08:53:26 +0900 +Subject: [PATCH] Randomize hash seed to mitigate hash collision DoS attacks + +The hash function used a fixed seed, allowing attackers to craft colliding keys +and cause O(n^2) object parsing performance. Initialize the seed from a random +source at process startup to prevent the attack. This fixes CVE-2026-40164. + +Co-authored-by: Asaf Meizner <asafmeizner@gmail.com> +CVE: CVE-2026-40164 +Upstream: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + configure.ac | 2 ++ + src/jv.c | 34 ++++++++++++++++++++++++++++++++-- + 2 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 5dac42655a..f7067a4341 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -143,6 +143,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define + AC_CHECK_MEMBER([struct tm.__tm_gmtoff], [AC_DEFINE([HAVE_TM___TM_GMT_OFF],1,[Define to 1 if the system has the __tm_gmt_off field in struct tm])], + [], [[#include <time.h>]]) + AC_FIND_FUNC([setlocale], [c], [#include <locale.h>], [0,0]) ++AC_FIND_FUNC([arc4random], [c], [#include <stdlib.h>], []) ++AC_FIND_FUNC([getentropy], [c], [#include <unistd.h>], [0, 0]) + + dnl Figure out if we have the pthread functions we actually need + AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include <pthread.h>], [NULL, NULL]) +diff --git a/src/jv.c b/src/jv.c +index 2a62b48419..607ac174f7 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -40,6 +40,10 @@ + #include <limits.h> + #include <math.h> + #include <float.h> ++#include <time.h> ++#include <unistd.h> ++#include <fcntl.h> ++#include <pthread.h> + + #include "jv_alloc.h" + #include "jv.h" +@@ -1206,7 +1210,33 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { + } + } + +-static const uint32_t HASH_SEED = 0x432A9843; ++static uint32_t hash_seed; ++static pthread_once_t hash_seed_once = PTHREAD_ONCE_INIT; ++ ++static void jvp_hash_seed_init(void) { ++ uint32_t seed; ++#if defined(HAVE_ARC4RANDOM) ++ seed = arc4random(); ++#elif defined(HAVE_GETENTROPY) ++ if (getentropy(&seed, sizeof(seed)) != 0) ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); ++#else ++ int fd = open("/dev/urandom", O_RDONLY); ++ if (fd >= 0) { ++ if (read(fd, &seed, sizeof(seed)) != 4) ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); ++ close(fd); ++ } else { ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); ++ } ++#endif ++ hash_seed = seed; ++} ++ ++static uint32_t jvp_hash_seed(void) { ++ pthread_once(&hash_seed_once, jvp_hash_seed_init); ++ return hash_seed; ++} + + static uint32_t rotl32 (uint32_t x, int8_t r){ + return (x << r) | (x >> (32 - r)); +@@ -1225,7 +1255,7 @@ static uint32_t jvp_string_hash(jv jstr) { + int len = (int)jvp_string_length(str); + const int nblocks = len / 4; + +- uint32_t h1 = HASH_SEED; ++ uint32_t h1 = jvp_hash_seed(); + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; diff --git a/package/jq/0006-limit-the-containment-check-depth.patch b/package/jq/0006-limit-the-containment-check-depth.patch new file mode 100644 index 0000000000..7834b8ed23 --- /dev/null +++ b/package/jq/0006-limit-the-containment-check-depth.patch @@ -0,0 +1,120 @@ +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. + +CVE: CVE-2026-40612 +Upstream: https://github.com/jqlang/jq/commit/d1a12569d91641135976a8536776a4a329c02cc2 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + 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 d33e9fb162..2b2a2d40da 100644 +--- a/src/builtin.c ++++ b/src/builtin.c +@@ -419,7 +419,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 607ac174f7..4b18c00cf6 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -938,19 +938,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; + } + } +@@ -1843,7 +1843,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; +@@ -1851,9 +1851,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; + } +@@ -2084,14 +2084,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) { +@@ -2107,3 +2116,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/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch b/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch new file mode 100644 index 0000000000..f67a7c8ed4 --- /dev/null +++ b/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch @@ -0,0 +1,34 @@ +From 5a015deae35d19e3ebbc65db6c157a80e76df738 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. + +CVE: CVE-2026-41256 +Upstream: https://github.com/jqlang/jq/commit/5a015deae35d19e3ebbc65db6c157a80e76df738 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/main.c | 8 ++++++++ + tests/shtest | 7 +++++++ + 2 files changed, 15 insertions(+) + +diff --git a/src/main.c b/src/main.c +index ce362607e2..fb5c7ab8e3 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -611,6 +611,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/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch b/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch new file mode 100644 index 0000000000..b0c39ed23b --- /dev/null +++ b/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch @@ -0,0 +1,53 @@ +From 01b3cded76daacbfddb7f8763700b0803bcb5c6f 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. + +CVE: CVE-2026-41257 +Upstream: https://github.com/jqlang/jq/commit/01b3cded76daacbfddb7f8763700b0803bcb5c6f +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + 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 2a063e8cf9..159c56e4fb 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) { diff --git a/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch b/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch new file mode 100644 index 0000000000..e1004d4707 --- /dev/null +++ b/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch @@ -0,0 +1,53 @@ +From 9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Wed, 6 May 2026 19:45:24 +0900 +Subject: [PATCH] Reject numeric literals longer than DEC_MAX_DIGITS + (999999999) + +A signed-int overflow in decNumber's D2U macro lets huge literals +write attacker-controlled bytes past a stack buffer. Cap the length +before calling decNumberFromString, and pre-slice long strings in +jv_dump_string_trunc so the resulting error message doesn't itself +allocate a multi-GiB buffer. + +Fixes CVE-2026-43894. + +CVE: CVE-2026-43894 +Upstream: https://github.com/jqlang/jq/commit/9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv.c | 5 ++++- + src/jv_print.c | 4 ++++ + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/src/jv.c b/src/jv.c +index 84fafef666..074ee310c5 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -578,7 +578,10 @@ static jvp_literal_number* jvp_literal_number_alloc(unsigned literal_length) { + } + + static jv jvp_literal_number_new(const char * literal) { +- jvp_literal_number* n = jvp_literal_number_alloc(strlen(literal)); ++ size_t len = strlen(literal); ++ if (len > DEC_MAX_DIGITS) ++ return JV_INVALID; ++ jvp_literal_number* n = jvp_literal_number_alloc(len); + + decContext *ctx = DEC_CONTEXT(); + decContextClearStatus(ctx, DEC_Conversion_syntax); +diff --git a/src/jv_print.c b/src/jv_print.c +index 5c86c5d97c..bc251070f7 100644 +--- a/src/jv_print.c ++++ b/src/jv_print.c +@@ -408,6 +408,10 @@ jv jv_dump_string(jv x, int flags) { + } + + char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) { ++ if (jv_get_kind(x) == JV_KIND_STRING && ++ (size_t)jv_string_length_bytes(jv_copy(x)) > bufsize) { ++ x = jv_string_slice(x, 0, bufsize); ++ } + x = jv_dump_string(x, 0); + const char *str = jv_string_value(x); + const size_t len = strlen(str); diff --git a/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch b/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch new file mode 100644 index 0000000000..11824a2d44 --- /dev/null +++ b/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch @@ -0,0 +1,1512 @@ +From 9d223f153c3632a207fa071caaa6292da33ae361 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Sat, 9 May 2026 17:08:43 +0900 +Subject: [PATCH] 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: https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/compile.c | 12 +- + src/compile.h | 2 +- + src/linker.c | 6 +- + src/parser.c | 556 +++++++++++++++++++++++++------------------------- + src/parser.y | 16 +- + tests/shtest | 17 ++ + 6 files changed, 307 insertions(+), 302 deletions(-) + +diff --git a/src/compile.c b/src/compile.c +index 5e64946b3e..80b723c119 100644 +--- a/src/compile.c ++++ b/src/compile.c +@@ -525,13 +525,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 bef46328a7..d195e9e2e8 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 cfd74d1d48..e9027004cc 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) { + jv home = get_home(); + if (jv_is_valid(home)) { + /* Import ~/.jq as a library named "" found in $HOME or %USERPROFILE% */ +- 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"), home))); +diff --git a/src/parser.c b/src/parser.c +index c90e313420..9c60173e27 100644 +--- a/src/parser.c ++++ b/src/parser.c +@@ -937,19 +937,19 @@ static const yytype_int16 yyrline[] = + 325, 328, 331, 337, 340, 343, 349, 352, 355, 358, + 361, 364, 367, 370, 373, 376, 379, 382, 385, 388, + 391, 394, 397, 400, 403, 406, 409, 412, 415, 421, +- 424, 441, 450, 457, 465, 476, 481, 487, 490, 495, +- 499, 506, 509, 515, 522, 525, 528, 534, 537, 540, +- 546, 549, 552, 560, 564, 567, 570, 573, 576, 579, +- 582, 585, 588, 592, 598, 601, 604, 607, 610, 613, +- 616, 619, 622, 625, 628, 631, 634, 637, 640, 643, +- 646, 649, 652, 655, 658, 661, 664, 671, 674, 677, +- 680, 683, 687, 690, 694, 712, 716, 720, 723, 735, +- 740, 741, 742, 743, 746, 749, 754, 759, 762, 767, +- 770, 775, 779, 782, 787, 790, 795, 798, 803, 806, +- 809, 812, 815, 818, 826, 832, 835, 838, 841, 844, +- 847, 850, 853, 856, 859, 862, 865, 868, 871, 874, +- 877, 880, 883, 889, 892, 895, 900, 903, 906, 909, +- 913, 918, 922, 926, 930, 934, 942, 948, 951 ++ 424, 441, 445, 449, 455, 466, 471, 477, 480, 485, ++ 489, 496, 499, 505, 512, 515, 518, 524, 527, 530, ++ 536, 539, 542, 550, 554, 557, 560, 563, 566, 569, ++ 572, 575, 578, 582, 588, 591, 594, 597, 600, 603, ++ 606, 609, 612, 615, 618, 621, 624, 627, 630, 633, ++ 636, 639, 642, 645, 648, 651, 654, 661, 664, 667, ++ 670, 673, 677, 680, 684, 702, 706, 710, 713, 725, ++ 730, 731, 732, 733, 736, 739, 744, 749, 752, 757, ++ 760, 765, 769, 772, 777, 780, 785, 788, 793, 796, ++ 799, 802, 805, 808, 816, 822, 825, 828, 831, 834, ++ 837, 840, 843, 846, 849, 852, 855, 858, 861, 864, ++ 867, 870, 873, 879, 882, 885, 890, 893, 896, 899, ++ 903, 908, 912, 916, 920, 924, 932, 938, 941 + }; + #endif + +@@ -2841,42 +2841,32 @@ YYLTYPE yylloc = yyloc_default; + case 41: /* ImportWhat: "import" ImportFrom "as" BINDING */ + #line 441 "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 2853 "src/parser.c" ++#line 2848 "src/parser.c" + break; + + case 42: /* ImportWhat: "import" ImportFrom "as" IDENT */ +-#line 450 "src/parser.y" ++#line 445 "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 2865 "src/parser.c" ++#line 2857 "src/parser.c" + break; + + case 43: /* ImportWhat: "include" ImportFrom */ +-#line 457 "src/parser.y" ++#line 449 "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 2876 "src/parser.c" ++#line 2866 "src/parser.c" + break; + + case 44: /* ImportFrom: String */ +-#line 465 "src/parser.y" ++#line 455 "src/parser.y" + { + if (!block_is_const((yyvsp[0].blk))) { + FAIL((yylsp[0]), "Import path must be constant"); +@@ -2886,152 +2876,152 @@ YYLTYPE yylloc = yyloc_default; + (yyval.blk) = (yyvsp[0].blk); + } + } +-#line 2890 "src/parser.c" ++#line 2880 "src/parser.c" + break; + + case 45: /* FuncDef: "def" IDENT ':' Query ';' */ +-#line 476 "src/parser.y" ++#line 466 "src/parser.y" + { + (yyval.blk) = gen_function(jv_string_value((yyvsp[-3].literal)), gen_noop(), (yyvsp[-1].blk)); + jv_free((yyvsp[-3].literal)); + } +-#line 2899 "src/parser.c" ++#line 2889 "src/parser.c" + break; + + case 46: /* FuncDef: "def" IDENT '(' Params ')' ':' Query ';' */ +-#line 481 "src/parser.y" ++#line 471 "src/parser.y" + { + (yyval.blk) = gen_function(jv_string_value((yyvsp[-6].literal)), (yyvsp[-4].blk), (yyvsp[-1].blk)); + jv_free((yyvsp[-6].literal)); + } +-#line 2908 "src/parser.c" ++#line 2898 "src/parser.c" + break; + + case 47: /* Params: Param */ +-#line 487 "src/parser.y" ++#line 477 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 2916 "src/parser.c" ++#line 2906 "src/parser.c" + break; + + case 48: /* Params: Params ';' Param */ +-#line 490 "src/parser.y" ++#line 480 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 2924 "src/parser.c" ++#line 2914 "src/parser.c" + break; + + case 49: /* Param: BINDING */ +-#line 495 "src/parser.y" ++#line 485 "src/parser.y" + { + (yyval.blk) = gen_param_regular(jv_string_value((yyvsp[0].literal))); + jv_free((yyvsp[0].literal)); + } +-#line 2933 "src/parser.c" ++#line 2923 "src/parser.c" + break; + + case 50: /* Param: IDENT */ +-#line 499 "src/parser.y" ++#line 489 "src/parser.y" + { + (yyval.blk) = gen_param(jv_string_value((yyvsp[0].literal))); + jv_free((yyvsp[0].literal)); + } +-#line 2942 "src/parser.c" ++#line 2932 "src/parser.c" + break; + + case 51: /* StringStart: FORMAT QQSTRING_START */ +-#line 506 "src/parser.y" ++#line 496 "src/parser.y" + { + (yyval.literal) = (yyvsp[-1].literal); + } +-#line 2950 "src/parser.c" ++#line 2940 "src/parser.c" + break; + + case 52: /* StringStart: QQSTRING_START */ +-#line 509 "src/parser.y" ++#line 499 "src/parser.y" + { + (yyval.literal) = jv_string("text"); + } +-#line 2958 "src/parser.c" ++#line 2948 "src/parser.c" + break; + + case 53: /* String: StringStart QQString QQSTRING_END */ +-#line 515 "src/parser.y" ++#line 505 "src/parser.y" + { + (yyval.blk) = (yyvsp[-1].blk); + jv_free((yyvsp[-2].literal)); + } +-#line 2967 "src/parser.c" ++#line 2957 "src/parser.c" + break; + + case 54: /* QQString: %empty */ +-#line 522 "src/parser.y" ++#line 512 "src/parser.y" + { + (yyval.blk) = gen_const(jv_string("")); + } +-#line 2975 "src/parser.c" ++#line 2965 "src/parser.c" + break; + + case 55: /* QQString: QQString QQSTRING_TEXT */ +-#line 525 "src/parser.y" ++#line 515 "src/parser.y" + { + (yyval.blk) = gen_binop((yyvsp[-1].blk), gen_const((yyvsp[0].literal)), '+'); + } +-#line 2983 "src/parser.c" ++#line 2973 "src/parser.c" + break; + + case 56: /* QQString: QQString QQSTRING_INTERP_START Query QQSTRING_INTERP_END */ +-#line 528 "src/parser.y" ++#line 518 "src/parser.y" + { + (yyval.blk) = gen_binop((yyvsp[-3].blk), gen_format((yyvsp[-1].blk), jv_copy((yyvsp[-4].literal))), '+'); + } +-#line 2991 "src/parser.c" ++#line 2981 "src/parser.c" + break; + + case 57: /* ElseBody: "elif" Query "then" Query ElseBody */ +-#line 534 "src/parser.y" ++#line 524 "src/parser.y" + { + (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); + } +-#line 2999 "src/parser.c" ++#line 2989 "src/parser.c" + break; + + case 58: /* ElseBody: "else" Query "end" */ +-#line 537 "src/parser.y" ++#line 527 "src/parser.y" + { + (yyval.blk) = (yyvsp[-1].blk); + } +-#line 3007 "src/parser.c" ++#line 2997 "src/parser.c" + break; + + case 59: /* ElseBody: "end" */ +-#line 540 "src/parser.y" ++#line 530 "src/parser.y" + { + (yyval.blk) = gen_noop(); + } +-#line 3015 "src/parser.c" ++#line 3005 "src/parser.c" + break; + + case 60: /* Term: '.' */ +-#line 546 "src/parser.y" ++#line 536 "src/parser.y" + { + (yyval.blk) = gen_noop(); + } +-#line 3023 "src/parser.c" ++#line 3013 "src/parser.c" + break; + + case 61: /* Term: ".." */ +-#line 549 "src/parser.y" ++#line 539 "src/parser.y" + { + (yyval.blk) = gen_call("recurse", gen_noop()); + } +-#line 3031 "src/parser.c" ++#line 3021 "src/parser.c" + break; + + case 62: /* Term: "break" BINDING */ +-#line 552 "src/parser.y" ++#line 542 "src/parser.y" + { + jv v = jv_string_fmt("*label-%s", jv_string_value((yyvsp[0].literal))); // impossible symbol + (yyval.blk) = gen_location((yyloc), locations, +@@ -3040,279 +3030,279 @@ YYLTYPE yylloc = yyloc_default; + jv_free(v); + jv_free((yyvsp[0].literal)); + } +-#line 3044 "src/parser.c" ++#line 3034 "src/parser.c" + break; + + case 63: /* Term: "break" error */ +-#line 560 "src/parser.y" ++#line 550 "src/parser.y" + { + FAIL((yyloc), "break requires a label to break to"); + (yyval.blk) = gen_noop(); + } +-#line 3053 "src/parser.c" ++#line 3043 "src/parser.c" + break; + + case 64: /* Term: Term FIELD '?' */ +-#line 564 "src/parser.y" ++#line 554 "src/parser.y" + { + (yyval.blk) = gen_index_opt((yyvsp[-2].blk), gen_const((yyvsp[-1].literal))); + } +-#line 3061 "src/parser.c" ++#line 3051 "src/parser.c" + break; + + case 65: /* Term: FIELD '?' */ +-#line 567 "src/parser.y" ++#line 557 "src/parser.y" + { + (yyval.blk) = gen_index_opt(gen_noop(), gen_const((yyvsp[-1].literal))); + } +-#line 3069 "src/parser.c" ++#line 3059 "src/parser.c" + break; + + case 66: /* Term: Term '.' String '?' */ +-#line 570 "src/parser.y" ++#line 560 "src/parser.y" + { + (yyval.blk) = gen_index_opt((yyvsp[-3].blk), (yyvsp[-1].blk)); + } +-#line 3077 "src/parser.c" ++#line 3067 "src/parser.c" + break; + + case 67: /* Term: '.' String '?' */ +-#line 573 "src/parser.y" ++#line 563 "src/parser.y" + { + (yyval.blk) = gen_index_opt(gen_noop(), (yyvsp[-1].blk)); + } +-#line 3085 "src/parser.c" ++#line 3075 "src/parser.c" + break; + + case 68: /* Term: Term FIELD */ +-#line 576 "src/parser.y" ++#line 566 "src/parser.y" + { + (yyval.blk) = gen_index((yyvsp[-1].blk), gen_const((yyvsp[0].literal))); + } +-#line 3093 "src/parser.c" ++#line 3083 "src/parser.c" + break; + + case 69: /* Term: FIELD */ +-#line 579 "src/parser.y" ++#line 569 "src/parser.y" + { + (yyval.blk) = gen_index(gen_noop(), gen_const((yyvsp[0].literal))); + } +-#line 3101 "src/parser.c" ++#line 3091 "src/parser.c" + break; + + case 70: /* Term: Term '.' String */ +-#line 582 "src/parser.y" ++#line 572 "src/parser.y" + { + (yyval.blk) = gen_index((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3109 "src/parser.c" ++#line 3099 "src/parser.c" + break; + + case 71: /* Term: '.' String */ +-#line 585 "src/parser.y" ++#line 575 "src/parser.y" + { + (yyval.blk) = gen_index(gen_noop(), (yyvsp[0].blk)); + } +-#line 3117 "src/parser.c" ++#line 3107 "src/parser.c" + break; + + case 72: /* Term: '.' error */ +-#line 588 "src/parser.y" ++#line 578 "src/parser.y" + { + FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields"); + (yyval.blk) = gen_noop(); + } +-#line 3126 "src/parser.c" ++#line 3116 "src/parser.c" + break; + + case 73: /* Term: '.' IDENT error */ +-#line 592 "src/parser.y" ++#line 582 "src/parser.y" + { + jv_free((yyvsp[-1].literal)); + FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields"); + (yyval.blk) = gen_noop(); + } +-#line 3136 "src/parser.c" ++#line 3126 "src/parser.c" + break; + + case 74: /* Term: Term '[' Query ']' '?' */ +-#line 598 "src/parser.y" ++#line 588 "src/parser.y" + { + (yyval.blk) = gen_index_opt((yyvsp[-4].blk), (yyvsp[-2].blk)); + } +-#line 3144 "src/parser.c" ++#line 3134 "src/parser.c" + break; + + case 75: /* Term: Term '[' Query ']' */ +-#line 601 "src/parser.y" ++#line 591 "src/parser.y" + { + (yyval.blk) = gen_index((yyvsp[-3].blk), (yyvsp[-1].blk)); + } +-#line 3152 "src/parser.c" ++#line 3142 "src/parser.c" + break; + + case 76: /* Term: Term '.' '[' Query ']' '?' */ +-#line 604 "src/parser.y" ++#line 594 "src/parser.y" + { + (yyval.blk) = gen_index_opt((yyvsp[-5].blk), (yyvsp[-2].blk)); + } +-#line 3160 "src/parser.c" ++#line 3150 "src/parser.c" + break; + + case 77: /* Term: Term '.' '[' Query ']' */ +-#line 607 "src/parser.y" ++#line 597 "src/parser.y" + { + (yyval.blk) = gen_index((yyvsp[-4].blk), (yyvsp[-1].blk)); + } +-#line 3168 "src/parser.c" ++#line 3158 "src/parser.c" + break; + + case 78: /* Term: Term '[' ']' '?' */ +-#line 610 "src/parser.y" ++#line 600 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH_OPT)); + } +-#line 3176 "src/parser.c" ++#line 3166 "src/parser.c" + break; + + case 79: /* Term: Term '[' ']' */ +-#line 613 "src/parser.y" ++#line 603 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-2].blk), gen_op_simple(EACH)); + } +-#line 3184 "src/parser.c" ++#line 3174 "src/parser.c" + break; + + case 80: /* Term: Term '.' '[' ']' '?' */ +-#line 616 "src/parser.y" ++#line 606 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-4].blk), gen_op_simple(EACH_OPT)); + } +-#line 3192 "src/parser.c" ++#line 3182 "src/parser.c" + break; + + case 81: /* Term: Term '.' '[' ']' */ +-#line 619 "src/parser.y" ++#line 609 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH)); + } +-#line 3200 "src/parser.c" ++#line 3190 "src/parser.c" + break; + + case 82: /* Term: Term '[' Query ':' Query ']' '?' */ +-#line 622 "src/parser.y" ++#line 612 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-6].blk), (yyvsp[-4].blk), (yyvsp[-2].blk), INDEX_OPT); + } +-#line 3208 "src/parser.c" ++#line 3198 "src/parser.c" + break; + + case 83: /* Term: Term '[' Query ':' ']' '?' */ +-#line 625 "src/parser.y" ++#line 615 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), gen_const(jv_null()), INDEX_OPT); + } +-#line 3216 "src/parser.c" ++#line 3206 "src/parser.c" + break; + + case 84: /* Term: Term '[' ':' Query ']' '?' */ +-#line 628 "src/parser.y" ++#line 618 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-5].blk), gen_const(jv_null()), (yyvsp[-2].blk), INDEX_OPT); + } +-#line 3224 "src/parser.c" ++#line 3214 "src/parser.c" + break; + + case 85: /* Term: Term '[' Query ':' Query ']' */ +-#line 631 "src/parser.y" ++#line 621 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), INDEX); + } +-#line 3232 "src/parser.c" ++#line 3222 "src/parser.c" + break; + + case 86: /* Term: Term '[' Query ':' ']' */ +-#line 634 "src/parser.y" ++#line 624 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-4].blk), (yyvsp[-2].blk), gen_const(jv_null()), INDEX); + } +-#line 3240 "src/parser.c" ++#line 3230 "src/parser.c" + break; + + case 87: /* Term: Term '[' ':' Query ']' */ +-#line 637 "src/parser.y" ++#line 627 "src/parser.y" + { + (yyval.blk) = gen_slice_index((yyvsp[-4].blk), gen_const(jv_null()), (yyvsp[-1].blk), INDEX); + } +-#line 3248 "src/parser.c" ++#line 3238 "src/parser.c" + break; + + case 88: /* Term: Term '?' */ +-#line 640 "src/parser.y" ++#line 630 "src/parser.y" + { + (yyval.blk) = gen_try((yyvsp[-1].blk), gen_op_simple(BACKTRACK)); + } +-#line 3256 "src/parser.c" ++#line 3246 "src/parser.c" + break; + + case 89: /* Term: LITERAL */ +-#line 643 "src/parser.y" ++#line 633 "src/parser.y" + { + (yyval.blk) = gen_const((yyvsp[0].literal)); + } +-#line 3264 "src/parser.c" ++#line 3254 "src/parser.c" + break; + + case 90: /* Term: String */ +-#line 646 "src/parser.y" ++#line 636 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3272 "src/parser.c" ++#line 3262 "src/parser.c" + break; + + case 91: /* Term: FORMAT */ +-#line 649 "src/parser.y" ++#line 639 "src/parser.y" + { + (yyval.blk) = gen_format(gen_noop(), (yyvsp[0].literal)); + } +-#line 3280 "src/parser.c" ++#line 3270 "src/parser.c" + break; + + case 92: /* Term: '-' Term */ +-#line 652 "src/parser.y" ++#line 642 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop())); + } +-#line 3288 "src/parser.c" ++#line 3278 "src/parser.c" + break; + + case 93: /* Term: '(' Query ')' */ +-#line 655 "src/parser.y" ++#line 645 "src/parser.y" + { + (yyval.blk) = (yyvsp[-1].blk); + } +-#line 3296 "src/parser.c" ++#line 3286 "src/parser.c" + break; + + case 94: /* Term: '[' Query ']' */ +-#line 658 "src/parser.y" ++#line 648 "src/parser.y" + { + (yyval.blk) = gen_collect((yyvsp[-1].blk)); + } +-#line 3304 "src/parser.c" ++#line 3294 "src/parser.c" + break; + + case 95: /* Term: '[' ']' */ +-#line 661 "src/parser.y" ++#line 651 "src/parser.y" + { + (yyval.blk) = gen_const(jv_array()); + } +-#line 3312 "src/parser.c" ++#line 3302 "src/parser.c" + break; + + case 96: /* Term: '{' DictPairs '}' */ +-#line 664 "src/parser.y" ++#line 654 "src/parser.y" + { + block o = gen_const_object((yyvsp[-1].blk)); + if (o.first != NULL) +@@ -3320,103 +3310,103 @@ YYLTYPE yylloc = yyloc_default; + else + (yyval.blk) = BLOCK(gen_subexp(gen_const(jv_object())), (yyvsp[-1].blk), gen_op_simple(POP)); + } +-#line 3324 "src/parser.c" ++#line 3314 "src/parser.c" + break; + + case 97: /* Term: "reduce" Expr "as" Patterns '(' Query ';' Query ')' */ +-#line 671 "src/parser.y" ++#line 661 "src/parser.y" + { + (yyval.blk) = gen_reduce((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk)); + } +-#line 3332 "src/parser.c" ++#line 3322 "src/parser.c" + break; + + case 98: /* Term: "foreach" Expr "as" Patterns '(' Query ';' Query ';' Query ')' */ +-#line 674 "src/parser.y" ++#line 664 "src/parser.y" + { + (yyval.blk) = gen_foreach((yyvsp[-9].blk), (yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk)); + } +-#line 3340 "src/parser.c" ++#line 3330 "src/parser.c" + break; + + case 99: /* Term: "foreach" Expr "as" Patterns '(' Query ';' Query ')' */ +-#line 677 "src/parser.y" ++#line 667 "src/parser.y" + { + (yyval.blk) = gen_foreach((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), gen_noop()); + } +-#line 3348 "src/parser.c" ++#line 3338 "src/parser.c" + break; + + case 100: /* Term: "if" Query "then" Query ElseBody */ +-#line 680 "src/parser.y" ++#line 670 "src/parser.y" + { + (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); + } +-#line 3356 "src/parser.c" ++#line 3346 "src/parser.c" + break; + + case 101: /* Term: "if" Query "then" error */ +-#line 683 "src/parser.y" ++#line 673 "src/parser.y" + { + FAIL((yyloc), "Possibly unterminated 'if' statement"); + (yyval.blk) = (yyvsp[-2].blk); + } +-#line 3365 "src/parser.c" ++#line 3355 "src/parser.c" + break; + + case 102: /* Term: "try" Expr "catch" Expr */ +-#line 687 "src/parser.y" ++#line 677 "src/parser.y" + { + (yyval.blk) = gen_try((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3373 "src/parser.c" ++#line 3363 "src/parser.c" + break; + + case 103: /* Term: "try" Expr "catch" error */ +-#line 690 "src/parser.y" ++#line 680 "src/parser.y" + { + FAIL((yyloc), "Possibly unterminated 'try' statement"); + (yyval.blk) = (yyvsp[-2].blk); + } +-#line 3382 "src/parser.c" ++#line 3372 "src/parser.c" + break; + + case 104: /* Term: "try" Expr */ +-#line 694 "src/parser.y" ++#line 684 "src/parser.y" + { + (yyval.blk) = gen_try((yyvsp[0].blk), gen_op_simple(BACKTRACK)); + } +-#line 3390 "src/parser.c" ++#line 3380 "src/parser.c" + break; + + case 105: /* Term: '$' '$' '$' BINDING */ +-#line 712 "src/parser.y" ++#line 702 "src/parser.y" + { + (yyval.blk) = gen_location((yyloc), locations, gen_op_unbound(LOADVN, jv_string_value((yyvsp[0].literal)))); + jv_free((yyvsp[0].literal)); + } +-#line 3399 "src/parser.c" ++#line 3389 "src/parser.c" + break; + + case 106: /* Term: BINDING */ +-#line 716 "src/parser.y" ++#line 706 "src/parser.y" + { + (yyval.blk) = gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal)))); + jv_free((yyvsp[0].literal)); + } +-#line 3408 "src/parser.c" ++#line 3398 "src/parser.c" + break; + + case 107: /* Term: "$__loc__" */ +-#line 720 "src/parser.y" ++#line 710 "src/parser.y" + { + (yyval.blk) = gen_loc_object(&(yyloc), locations); + } +-#line 3416 "src/parser.c" ++#line 3406 "src/parser.c" + break; + + case 108: /* Term: IDENT */ +-#line 723 "src/parser.y" ++#line 713 "src/parser.y" + { + const char *s = jv_string_value((yyvsp[0].literal)); + if (strcmp(s, "false") == 0) +@@ -3429,198 +3419,198 @@ YYLTYPE yylloc = yyloc_default; + (yyval.blk) = gen_location((yyloc), locations, gen_call(s, gen_noop())); + jv_free((yyvsp[0].literal)); + } +-#line 3433 "src/parser.c" ++#line 3423 "src/parser.c" + break; + + case 109: /* Term: IDENT '(' Args ')' */ +-#line 735 "src/parser.y" ++#line 725 "src/parser.y" + { + (yyval.blk) = gen_call(jv_string_value((yyvsp[-3].literal)), (yyvsp[-1].blk)); + (yyval.blk) = gen_location((yylsp[-3]), locations, (yyval.blk)); + jv_free((yyvsp[-3].literal)); + } +-#line 3443 "src/parser.c" ++#line 3433 "src/parser.c" + break; + + case 110: /* Term: '(' error ')' */ +-#line 740 "src/parser.y" ++#line 730 "src/parser.y" + { (yyval.blk) = gen_noop(); } +-#line 3449 "src/parser.c" ++#line 3439 "src/parser.c" + break; + + case 111: /* Term: '[' error ']' */ +-#line 741 "src/parser.y" ++#line 731 "src/parser.y" + { (yyval.blk) = gen_noop(); } +-#line 3455 "src/parser.c" ++#line 3445 "src/parser.c" + break; + + case 112: /* Term: Term '[' error ']' */ +-#line 742 "src/parser.y" ++#line 732 "src/parser.y" + { (yyval.blk) = (yyvsp[-3].blk); } +-#line 3461 "src/parser.c" ++#line 3451 "src/parser.c" + break; + + case 113: /* Term: '{' error '}' */ +-#line 743 "src/parser.y" ++#line 733 "src/parser.y" + { (yyval.blk) = gen_noop(); } +-#line 3467 "src/parser.c" ++#line 3457 "src/parser.c" + break; + + case 114: /* Args: Arg */ +-#line 746 "src/parser.y" ++#line 736 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3475 "src/parser.c" ++#line 3465 "src/parser.c" + break; + + case 115: /* Args: Args ';' Arg */ +-#line 749 "src/parser.y" ++#line 739 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3483 "src/parser.c" ++#line 3473 "src/parser.c" + break; + + case 116: /* Arg: Query */ +-#line 754 "src/parser.y" ++#line 744 "src/parser.y" + { + (yyval.blk) = gen_lambda((yyvsp[0].blk)); + } +-#line 3491 "src/parser.c" ++#line 3481 "src/parser.c" + break; + + case 117: /* RepPatterns: RepPatterns "?//" Pattern */ +-#line 759 "src/parser.y" ++#line 749 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-2].blk), gen_destructure_alt((yyvsp[0].blk))); + } +-#line 3499 "src/parser.c" ++#line 3489 "src/parser.c" + break; + + case 118: /* RepPatterns: Pattern */ +-#line 762 "src/parser.y" ++#line 752 "src/parser.y" + { + (yyval.blk) = gen_destructure_alt((yyvsp[0].blk)); + } +-#line 3507 "src/parser.c" ++#line 3497 "src/parser.c" + break; + + case 119: /* Patterns: RepPatterns "?//" Pattern */ +-#line 767 "src/parser.y" ++#line 757 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3515 "src/parser.c" ++#line 3505 "src/parser.c" + break; + + case 120: /* Patterns: Pattern */ +-#line 770 "src/parser.y" ++#line 760 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3523 "src/parser.c" ++#line 3513 "src/parser.c" + break; + + case 121: /* Pattern: BINDING */ +-#line 775 "src/parser.y" ++#line 765 "src/parser.y" + { + (yyval.blk) = gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal))); + jv_free((yyvsp[0].literal)); + } +-#line 3532 "src/parser.c" ++#line 3522 "src/parser.c" + break; + + case 122: /* Pattern: '[' ArrayPats ']' */ +-#line 779 "src/parser.y" ++#line 769 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); + } +-#line 3540 "src/parser.c" ++#line 3530 "src/parser.c" + break; + + case 123: /* Pattern: '{' ObjPats '}' */ +-#line 782 "src/parser.y" ++#line 772 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); + } +-#line 3548 "src/parser.c" ++#line 3538 "src/parser.c" + break; + + case 124: /* ArrayPats: Pattern */ +-#line 787 "src/parser.y" ++#line 777 "src/parser.y" + { + (yyval.blk) = gen_array_matcher(gen_noop(), (yyvsp[0].blk)); + } +-#line 3556 "src/parser.c" ++#line 3546 "src/parser.c" + break; + + case 125: /* ArrayPats: ArrayPats ',' Pattern */ +-#line 790 "src/parser.y" ++#line 780 "src/parser.y" + { + (yyval.blk) = gen_array_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3564 "src/parser.c" ++#line 3554 "src/parser.c" + break; + + case 126: /* ObjPats: ObjPat */ +-#line 795 "src/parser.y" ++#line 785 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3572 "src/parser.c" ++#line 3562 "src/parser.c" + break; + + case 127: /* ObjPats: ObjPats ',' ObjPat */ +-#line 798 "src/parser.y" ++#line 788 "src/parser.y" + { + (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3580 "src/parser.c" ++#line 3570 "src/parser.c" + break; + + case 128: /* ObjPat: BINDING */ +-#line 803 "src/parser.y" ++#line 793 "src/parser.y" + { + (yyval.blk) = gen_object_matcher(gen_const((yyvsp[0].literal)), gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal)))); + } +-#line 3588 "src/parser.c" ++#line 3578 "src/parser.c" + break; + + case 129: /* ObjPat: BINDING ':' Pattern */ +-#line 806 "src/parser.y" ++#line 796 "src/parser.y" + { + (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), BLOCK(gen_op_simple(DUP), gen_op_unbound(STOREV, jv_string_value((yyvsp[-2].literal))), (yyvsp[0].blk))); + } +-#line 3596 "src/parser.c" ++#line 3586 "src/parser.c" + break; + + case 130: /* ObjPat: IDENT ':' Pattern */ +-#line 809 "src/parser.y" ++#line 799 "src/parser.y" + { + (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); + } +-#line 3604 "src/parser.c" ++#line 3594 "src/parser.c" + break; + + case 131: /* ObjPat: Keyword ':' Pattern */ +-#line 812 "src/parser.y" ++#line 802 "src/parser.y" + { + (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); + } +-#line 3612 "src/parser.c" ++#line 3602 "src/parser.c" + break; + + case 132: /* ObjPat: String ':' Pattern */ +-#line 815 "src/parser.y" ++#line 805 "src/parser.y" + { + (yyval.blk) = gen_object_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3620 "src/parser.c" ++#line 3610 "src/parser.c" + break; + + case 133: /* ObjPat: '(' Query ')' ':' Pattern */ +-#line 818 "src/parser.y" ++#line 808 "src/parser.y" + { + jv msg = check_object_key((yyvsp[-3].blk)); + if (jv_is_valid(msg)) { +@@ -3629,267 +3619,267 @@ YYLTYPE yylloc = yyloc_default; + jv_free(msg); + (yyval.blk) = gen_object_matcher((yyvsp[-3].blk), (yyvsp[0].blk)); + } +-#line 3633 "src/parser.c" ++#line 3623 "src/parser.c" + break; + + case 134: /* ObjPat: error ':' Pattern */ +-#line 826 "src/parser.y" ++#line 816 "src/parser.y" + { + FAIL((yyloc), "May need parentheses around object key expression"); + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3642 "src/parser.c" ++#line 3632 "src/parser.c" + break; + + case 135: /* Keyword: "as" */ +-#line 832 "src/parser.y" ++#line 822 "src/parser.y" + { + (yyval.literal) = jv_string("as"); + } +-#line 3650 "src/parser.c" ++#line 3640 "src/parser.c" + break; + + case 136: /* Keyword: "def" */ +-#line 835 "src/parser.y" ++#line 825 "src/parser.y" + { + (yyval.literal) = jv_string("def"); + } +-#line 3658 "src/parser.c" ++#line 3648 "src/parser.c" + break; + + case 137: /* Keyword: "module" */ +-#line 838 "src/parser.y" ++#line 828 "src/parser.y" + { + (yyval.literal) = jv_string("module"); + } +-#line 3666 "src/parser.c" ++#line 3656 "src/parser.c" + break; + + case 138: /* Keyword: "import" */ +-#line 841 "src/parser.y" ++#line 831 "src/parser.y" + { + (yyval.literal) = jv_string("import"); + } +-#line 3674 "src/parser.c" ++#line 3664 "src/parser.c" + break; + + case 139: /* Keyword: "include" */ +-#line 844 "src/parser.y" ++#line 834 "src/parser.y" + { + (yyval.literal) = jv_string("include"); + } +-#line 3682 "src/parser.c" ++#line 3672 "src/parser.c" + break; + + case 140: /* Keyword: "if" */ +-#line 847 "src/parser.y" ++#line 837 "src/parser.y" + { + (yyval.literal) = jv_string("if"); + } +-#line 3690 "src/parser.c" ++#line 3680 "src/parser.c" + break; + + case 141: /* Keyword: "then" */ +-#line 850 "src/parser.y" ++#line 840 "src/parser.y" + { + (yyval.literal) = jv_string("then"); + } +-#line 3698 "src/parser.c" ++#line 3688 "src/parser.c" + break; + + case 142: /* Keyword: "else" */ +-#line 853 "src/parser.y" ++#line 843 "src/parser.y" + { + (yyval.literal) = jv_string("else"); + } +-#line 3706 "src/parser.c" ++#line 3696 "src/parser.c" + break; + + case 143: /* Keyword: "elif" */ +-#line 856 "src/parser.y" ++#line 846 "src/parser.y" + { + (yyval.literal) = jv_string("elif"); + } +-#line 3714 "src/parser.c" ++#line 3704 "src/parser.c" + break; + + case 144: /* Keyword: "reduce" */ +-#line 859 "src/parser.y" ++#line 849 "src/parser.y" + { + (yyval.literal) = jv_string("reduce"); + } +-#line 3722 "src/parser.c" ++#line 3712 "src/parser.c" + break; + + case 145: /* Keyword: "foreach" */ +-#line 862 "src/parser.y" ++#line 852 "src/parser.y" + { + (yyval.literal) = jv_string("foreach"); + } +-#line 3730 "src/parser.c" ++#line 3720 "src/parser.c" + break; + + case 146: /* Keyword: "end" */ +-#line 865 "src/parser.y" ++#line 855 "src/parser.y" + { + (yyval.literal) = jv_string("end"); + } +-#line 3738 "src/parser.c" ++#line 3728 "src/parser.c" + break; + + case 147: /* Keyword: "and" */ +-#line 868 "src/parser.y" ++#line 858 "src/parser.y" + { + (yyval.literal) = jv_string("and"); + } +-#line 3746 "src/parser.c" ++#line 3736 "src/parser.c" + break; + + case 148: /* Keyword: "or" */ +-#line 871 "src/parser.y" ++#line 861 "src/parser.y" + { + (yyval.literal) = jv_string("or"); + } +-#line 3754 "src/parser.c" ++#line 3744 "src/parser.c" + break; + + case 149: /* Keyword: "try" */ +-#line 874 "src/parser.y" ++#line 864 "src/parser.y" + { + (yyval.literal) = jv_string("try"); + } +-#line 3762 "src/parser.c" ++#line 3752 "src/parser.c" + break; + + case 150: /* Keyword: "catch" */ +-#line 877 "src/parser.y" ++#line 867 "src/parser.y" + { + (yyval.literal) = jv_string("catch"); + } +-#line 3770 "src/parser.c" ++#line 3760 "src/parser.c" + break; + + case 151: /* Keyword: "label" */ +-#line 880 "src/parser.y" ++#line 870 "src/parser.y" + { + (yyval.literal) = jv_string("label"); + } +-#line 3778 "src/parser.c" ++#line 3768 "src/parser.c" + break; + + case 152: /* Keyword: "break" */ +-#line 883 "src/parser.y" ++#line 873 "src/parser.y" + { + (yyval.literal) = jv_string("break"); + } +-#line 3786 "src/parser.c" ++#line 3776 "src/parser.c" + break; + + case 153: /* DictPairs: %empty */ +-#line 889 "src/parser.y" ++#line 879 "src/parser.y" + { + (yyval.blk) = gen_noop(); + } +-#line 3794 "src/parser.c" ++#line 3784 "src/parser.c" + break; + + case 154: /* DictPairs: DictPair */ +-#line 892 "src/parser.y" ++#line 882 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3802 "src/parser.c" ++#line 3792 "src/parser.c" + break; + + case 155: /* DictPairs: DictPair ',' DictPairs */ +-#line 895 "src/parser.y" ++#line 885 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3810 "src/parser.c" ++#line 3800 "src/parser.c" + break; + + case 156: /* DictPair: IDENT ':' DictExpr */ +-#line 900 "src/parser.y" ++#line 890 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); + } +-#line 3818 "src/parser.c" ++#line 3808 "src/parser.c" + break; + + case 157: /* DictPair: Keyword ':' DictExpr */ +-#line 903 "src/parser.y" ++#line 893 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); + } +-#line 3826 "src/parser.c" ++#line 3816 "src/parser.c" + break; + + case 158: /* DictPair: String ':' DictExpr */ +-#line 906 "src/parser.y" ++#line 896 "src/parser.y" + { + (yyval.blk) = gen_dictpair((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3834 "src/parser.c" ++#line 3824 "src/parser.c" + break; + + case 159: /* DictPair: String */ +-#line 909 "src/parser.y" ++#line 899 "src/parser.y" + { + (yyval.blk) = gen_dictpair((yyvsp[0].blk), BLOCK(gen_op_simple(POP), gen_op_simple(DUP2), + gen_op_simple(DUP2), gen_op_simple(INDEX))); + } +-#line 3843 "src/parser.c" ++#line 3833 "src/parser.c" + break; + + case 160: /* DictPair: BINDING ':' DictExpr */ +-#line 913 "src/parser.y" ++#line 903 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[-2].literal)))), + (yyvsp[0].blk)); + jv_free((yyvsp[-2].literal)); + } +-#line 3853 "src/parser.c" ++#line 3843 "src/parser.c" + break; + + case 161: /* DictPair: BINDING */ +-#line 918 "src/parser.y" ++#line 908 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const((yyvsp[0].literal)), + gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal))))); + } +-#line 3862 "src/parser.c" ++#line 3852 "src/parser.c" + break; + + case 162: /* DictPair: IDENT */ +-#line 922 "src/parser.y" ++#line 912 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const(jv_copy((yyvsp[0].literal))), + gen_index(gen_noop(), gen_const((yyvsp[0].literal)))); + } +-#line 3871 "src/parser.c" ++#line 3861 "src/parser.c" + break; + + case 163: /* DictPair: "$__loc__" */ +-#line 926 "src/parser.y" ++#line 916 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const(jv_string("__loc__")), + gen_loc_object(&(yyloc), locations)); + } +-#line 3880 "src/parser.c" ++#line 3870 "src/parser.c" + break; + + case 164: /* DictPair: Keyword */ +-#line 930 "src/parser.y" ++#line 920 "src/parser.y" + { + (yyval.blk) = gen_dictpair(gen_const(jv_copy((yyvsp[0].literal))), + gen_index(gen_noop(), gen_const((yyvsp[0].literal)))); + } +-#line 3889 "src/parser.c" ++#line 3879 "src/parser.c" + break; + + case 165: /* DictPair: '(' Query ')' ':' DictExpr */ +-#line 934 "src/parser.y" ++#line 924 "src/parser.y" + { + jv msg = check_object_key((yyvsp[-3].blk)); + if (jv_is_valid(msg)) { +@@ -3898,36 +3888,36 @@ YYLTYPE yylloc = yyloc_default; + jv_free(msg); + (yyval.blk) = gen_dictpair((yyvsp[-3].blk), (yyvsp[0].blk)); + } +-#line 3902 "src/parser.c" ++#line 3892 "src/parser.c" + break; + + case 166: /* DictPair: error ':' DictExpr */ +-#line 942 "src/parser.y" ++#line 932 "src/parser.y" + { + FAIL((yylsp[-2]), "May need parentheses around object key expression"); + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3911 "src/parser.c" ++#line 3901 "src/parser.c" + break; + + case 167: /* DictExpr: DictExpr '|' DictExpr */ +-#line 948 "src/parser.y" ++#line 938 "src/parser.y" + { + (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); + } +-#line 3919 "src/parser.c" ++#line 3909 "src/parser.c" + break; + + case 168: /* DictExpr: Expr */ +-#line 951 "src/parser.y" ++#line 941 "src/parser.y" + { + (yyval.blk) = (yyvsp[0].blk); + } +-#line 3927 "src/parser.c" ++#line 3917 "src/parser.c" + break; + + +-#line 3931 "src/parser.c" ++#line 3921 "src/parser.c" + + default: break; + } +@@ -4156,7 +4146,7 @@ YYLTYPE yylloc = yyloc_default; + return yyresult; + } + +-#line 954 "src/parser.y" ++#line 944 "src/parser.y" + + + int jq_parse(struct locfile* locations, block* answer) { +diff --git a/src/parser.y b/src/parser.y +index 987a4ecaa3..ecd5796561 100644 +--- a/src/parser.y ++++ b/src/parser.y +@@ -439,26 +439,16 @@ ImportWhat Query ';' { + + 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/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch b/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch new file mode 100644 index 0000000000..5f2f10d8af --- /dev/null +++ b/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch @@ -0,0 +1,66 @@ +From 532ccea6080ed6758f39fe9f6208a44b665023d2 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Tue, 5 May 2026 22:44:02 +0900 +Subject: [PATCH] Limit recursive object merge depth to prevent stack overflow + +This fixes CVE-2026-43896. + +CVE: CVE-2026-43896 +Upstream: https://github.com/jqlang/jq/commit/532ccea6080ed6758f39fe9f6208a44b665023d2 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv.c | 25 +++++++++++++++++++++++-- + tests/jq.test | 9 +++++++++ + 2 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/src/jv.c b/src/jv.c +index feb68d1a1c..84fafef666 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1933,16 +1933,33 @@ jv jv_object_merge(jv a, jv b) { + return a; + } + +-jv jv_object_merge_recursive(jv a, jv b) { ++#ifndef MAX_OBJECT_MERGE_DEPTH ++#define MAX_OBJECT_MERGE_DEPTH (10000) ++#endif ++ ++static jv jvp_object_merge_recursive(jv a, jv b, int depth) { + assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); + assert(JVP_HAS_KIND(b, JV_KIND_OBJECT)); + ++ if (depth > MAX_OBJECT_MERGE_DEPTH) { ++ jv_free(a); ++ jv_free(b); ++ return jv_invalid_with_msg(jv_string("Object merge too deep")); ++ } ++ + jv_object_foreach(b, k, v) { + jv elem = jv_object_get(jv_copy(a), jv_copy(k)); + if (jv_is_valid(elem) && + JVP_HAS_KIND(elem, JV_KIND_OBJECT) && + JVP_HAS_KIND(v, JV_KIND_OBJECT)) { +- a = jv_object_set(a, k, jv_object_merge_recursive(elem, v)); ++ jv merged = jvp_object_merge_recursive(elem, v, depth + 1); ++ if (!jv_is_valid(merged)) { ++ jv_free(k); ++ jv_free(a); ++ jv_free(b); ++ return merged; ++ } ++ a = jv_object_set(a, k, merged); + } else { + jv_free(elem); + a = jv_object_set(a, k, v); +@@ -1953,6 +1970,10 @@ jv jv_object_merge_recursive(jv a, jv b) { + return a; + } + ++jv jv_object_merge_recursive(jv a, jv b) { ++ return jvp_object_merge_recursive(a, b, 0); ++} ++ + /* + * Object iteration (internal helpers) + */ diff --git a/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch b/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch new file mode 100644 index 0000000000..ca82a3a744 --- /dev/null +++ b/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch @@ -0,0 +1,142 @@ +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: https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/linker.c | 59 ++++++++++++++++++++++++------------- + 6 files changed, 70 insertions(+), 20 deletions(-) + +diff --git a/src/linker.c b/src/linker.c +index e9027004cc..03f46db05c 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, +@@ -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,30 +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; + } + } +- 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); +@@ -413,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; +@@ -439,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/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch b/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch new file mode 100644 index 0000000000..bfcb6a12b2 --- /dev/null +++ b/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch @@ -0,0 +1,423 @@ +From 7122866869960b55cea3646bc91334ef55787831 Mon Sep 17 00:00:00 2001 +From: Yu-Fu Fu <yufu@yfu.tw> +Date: Fri, 22 May 2026 04:07:16 -0700 +Subject: [PATCH] Guard deep structural equality and comparison recursion + (#3539) + +jv_equal and jv_cmp overflows the C stack on deeply nested +input. Cap recursion at 10000 with -1 / INT_MIN sentinels; +operators that compose user expressions surface this as +"Equality check too deep" / "Comparison too deep". + +Fixes CVE-2026-47770. + +CVE: CVE-2026-47770 +Upstream: https://github.com/jqlang/jq/commit/7122866869960b55cea3646bc91334ef55787831 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/builtin.c | 36 +++++++++++++++-- + src/jv.c | 46 +++++++++++++++++----- + src/jv_aux.c | 104 ++++++++++++++++++++++++++++++++++++++++++-------- + 4 files changed, 180 insertions(+), 28 deletions(-) + +diff --git a/src/builtin.c b/src/builtin.c +index 2b2a2d40da..8ee605ed2b 100644 +--- a/src/builtin.c ++++ b/src/builtin.c +@@ -295,7 +295,15 @@ jv binop_minus(jv a, jv b) { + jv_array_foreach(a, i, x) { + int include = 1; + jv_array_foreach(b, j, y) { +- if (jv_equal(jv_copy(x), y)) { ++ int equal = jv_equal(jv_copy(x), y); ++ if (equal < 0) { ++ jv_free(out); ++ jv_free(x); ++ jv_free(a); ++ jv_free(b); ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ } ++ if (equal) { + include = 0; + break; + } +@@ -379,11 +387,17 @@ jv binop_mod(jv a, jv b) { + #undef dtoi + + jv binop_equal(jv a, jv b) { +- return jv_bool(jv_equal(a, b)); ++ int r = jv_equal(a, b); ++ if (r < 0) ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ return jv_bool(r); + } + + jv binop_notequal(jv a, jv b) { +- return jv_bool(!jv_equal(a, b)); ++ int r = jv_equal(a, b); ++ if (r < 0) ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ return jv_bool(!r); + } + + enum cmp_op { +@@ -395,6 +409,8 @@ enum cmp_op { + + static jv order_cmp(jv a, jv b, enum cmp_op op) { + int r = jv_cmp(a, b); ++ if (r == INT_MIN) ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); + return jv_bool((op == CMP_OP_LESS && r < 0) || + (op == CMP_OP_LESSEQ && r <= 0) || + (op == CMP_OP_GREATEREQ && r >= 0) || +@@ -848,6 +864,12 @@ static jv f_bsearch(jq_state *jq, jv input, jv target) { + while (start < end) { + int mid = start + (end - start) / 2; + int result = jv_cmp(jv_copy(target), jv_array_get(jv_copy(input), mid)); ++ if (result == INT_MIN) { ++ jv_free(answer); ++ jv_free(input); ++ jv_free(target); ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); ++ } + if (result == 0) { + answer = jv_number(mid); + break; +@@ -1139,6 +1161,14 @@ static jv minmax_by(jv values, jv keys, int is_min) { + for (int i=1; i<jv_array_length(jv_copy(values)); i++) { + jv item = jv_array_get(jv_copy(keys), i); + int cmp = jv_cmp(jv_copy(item), jv_copy(retkey)); ++ if (cmp == INT_MIN) { ++ jv_free(item); ++ jv_free(values); ++ jv_free(keys); ++ jv_free(retkey); ++ jv_free(ret); ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); ++ } + if ((cmp < 0) == (is_min == 1)) { + jv_free(retkey); + retkey = item; +diff --git a/src/jv.c b/src/jv.c +index 074ee310c5..bcd7c9b5b0 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -915,16 +915,20 @@ static jv* jvp_array_write(jv* a, int i) { + } + } + +-static int jvp_array_equal(jv a, jv b) { ++static int jvp_equal(jv a, jv b, int depth); ++ ++static int jvp_array_equal(jv a, jv b, int depth) { + if (jvp_array_length(a) != jvp_array_length(b)) + return 0; + if (jvp_array_ptr(a) == jvp_array_ptr(b) && + jvp_array_offset(a) == jvp_array_offset(b)) + return 1; + for (int i=0; i<jvp_array_length(a); i++) { +- if (!jv_equal(jv_copy(*jvp_array_read(a, i)), +- jv_copy(*jvp_array_read(b, i)))) +- return 0; ++ int r = jvp_equal(jv_copy(*jvp_array_read(a, i)), ++ jv_copy(*jvp_array_read(b, i)), ++ depth); ++ if (r <= 0) ++ return r; + } + return 1; + } +@@ -1074,7 +1078,14 @@ jv jv_array_indexes(jv a, jv b) { + int alen = jv_array_length(jv_copy(a)); + for (int ai = 0; ai < alen; ++ai) { + jv_array_foreach(b, bi, belem) { +- if (!jv_equal(jv_array_get(jv_copy(a), ai + bi), belem)) ++ int equal = jv_equal(jv_array_get(jv_copy(a), ai + bi), belem); ++ if (equal < 0) { ++ jv_free(res); ++ jv_free(a); ++ jv_free(b); ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ } ++ if (!equal) + idx = -1; + else if (bi == 0 && idx == -1) + idx = ai; +@@ -1831,7 +1842,7 @@ static int jvp_object_length(jv object) { + return n; + } + +-static int jvp_object_equal(jv o1, jv o2) { ++static int jvp_object_equal(jv o1, jv o2, int depth) { + int len2 = jvp_object_length(o2); + int len1 = 0; + for (int i=0; i<jvp_object_size(o1); i++) { +@@ -1840,7 +1851,8 @@ static int jvp_object_equal(jv o1, jv o2) { + jv* slot2 = jvp_object_read(o2, slot->string); + if (!slot2) return 0; + // FIXME: do less refcounting here +- if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0; ++ int r = jvp_equal(jv_copy(slot->value), jv_copy(*slot2), depth); ++ if (r <= 0) return r; + len1++; + } + return len1 == len2; +@@ -2056,7 +2068,16 @@ int jv_get_refcnt(jv j) { + * Higher-level operations + */ + +-int jv_equal(jv a, jv b) { ++#ifndef MAX_EQUAL_DEPTH ++#define MAX_EQUAL_DEPTH (10000) ++#endif ++ ++static int jvp_equal(jv a, jv b, int depth) { ++ if (depth > MAX_EQUAL_DEPTH) { ++ jv_free(a); ++ jv_free(b); ++ return -1; ++ } + int r; + if (jv_get_kind(a) != jv_get_kind(b)) { + r = 0; +@@ -2072,13 +2093,13 @@ int jv_equal(jv a, jv b) { + r = jvp_number_equal(a, b); + break; + case JV_KIND_ARRAY: +- r = jvp_array_equal(a, b); ++ r = jvp_array_equal(a, b, depth + 1); + break; + case JV_KIND_STRING: + r = jvp_string_equal(a, b); + break; + case JV_KIND_OBJECT: +- r = jvp_object_equal(a, b); ++ r = jvp_object_equal(a, b, depth + 1); + break; + default: + r = 1; +@@ -2090,6 +2111,11 @@ int jv_equal(jv a, jv b) { + return r; + } + ++// Returns 1 if equal, 0 if not equal, or -1 if the comparison is too deep ++int jv_equal(jv a, jv b) { ++ return jvp_equal(a, b, 0); ++} ++ + int jv_identical(jv a, jv b) { + int r; + if (a.kind_flags != b.kind_flags +diff --git a/src/jv_aux.c b/src/jv_aux.c +index fd5ff96684..4f5be0ab14 100644 +--- a/src/jv_aux.c ++++ b/src/jv_aux.c +@@ -16,6 +16,24 @@ static double jv_number_get_value_and_consume(jv number) { + return value; + } + ++#ifndef MAX_CMP_DEPTH ++#define MAX_CMP_DEPTH (10000) ++#endif ++ ++struct sort_cmp_state { ++ int too_deep; ++}; ++ ++#ifdef _MSC_VER ++static __declspec(thread) struct sort_cmp_state sort_cmp_state; ++#else ++#ifdef HAVE___THREAD ++static __thread struct sort_cmp_state sort_cmp_state; ++#else ++static struct sort_cmp_state sort_cmp_state; ++#endif ++#endif ++ + static jv parse_slice(jv j, jv slice, int* pstart, int* pend) { + // Array slices + jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); +@@ -606,7 +624,13 @@ jv jv_keys(jv x) { + } + } + +-int jv_cmp(jv a, jv b) { ++static int jvp_cmp(jv a, jv b, int depth) { ++ if (depth > MAX_CMP_DEPTH) { ++ jv_free(a); ++ jv_free(b); ++ return INT_MIN; ++ } ++ + if (jv_get_kind(a) != jv_get_kind(b)) { + int r = (int)jv_get_kind(a) - (int)jv_get_kind(b); + jv_free(a); +@@ -621,14 +645,13 @@ int jv_cmp(jv a, jv b) { + case JV_KIND_FALSE: + case JV_KIND_TRUE: + // there's only one of each of these values +- r = 0; + break; + + case JV_KIND_NUMBER: { + if (jvp_number_is_nan(a)) { +- r = jv_cmp(jv_null(), jv_copy(b)); ++ r = jvp_cmp(jv_null(), jv_copy(b), depth); + } else if (jvp_number_is_nan(b)) { +- r = jv_cmp(jv_copy(a), jv_null()); ++ r = jvp_cmp(jv_copy(a), jv_null(), depth); + } else { + r = jvp_number_cmp(a, b); + } +@@ -652,7 +675,9 @@ int jv_cmp(jv a, jv b) { + } + jv xa = jv_array_get(jv_copy(a), i); + jv xb = jv_array_get(jv_copy(b), i); +- r = jv_cmp(xa, xb); ++ r = jvp_cmp(xa, xb, depth + 1); ++ if (r == INT_MIN) ++ break; + i++; + } + break; +@@ -661,13 +686,14 @@ int jv_cmp(jv a, jv b) { + case JV_KIND_OBJECT: { + jv keys_a = jv_keys(jv_copy(a)); + jv keys_b = jv_keys(jv_copy(b)); +- r = jv_cmp(jv_copy(keys_a), keys_b); ++ r = jvp_cmp(jv_copy(keys_a), keys_b, depth + 1); + if (r == 0) { + jv_array_foreach(keys_a, i, key) { + jv xa = jv_object_get(jv_copy(a), jv_copy(key)); + jv xb = jv_object_get(jv_copy(b), key); +- r = jv_cmp(xa, xb); +- if (r) break; ++ r = jvp_cmp(xa, xb, depth + 1); ++ if (r != 0) ++ break; + } + } + jv_free(keys_a); +@@ -680,6 +706,11 @@ int jv_cmp(jv a, jv b) { + return r; + } + ++// Returns <0, 0, >0 if a is less than, equal to, or greater than b, or ++// INT_MIN if the comparison is too deep ++int jv_cmp(jv a, jv b) { ++ return jvp_cmp(a, b, 0); ++} + + struct sort_entry { + jv object; +@@ -687,19 +718,32 @@ struct sort_entry { + int index; + }; + ++static void sort_entry_array_free(struct sort_entry* entries, int start, int n) { ++ for (int i = start; i < n; i++) { ++ jv_free(entries[i].key); ++ jv_free(entries[i].object); ++ } ++ jv_mem_free(entries); ++} ++ + static int sort_cmp(const void* pa, const void* pb) { + const struct sort_entry* a = pa; + const struct sort_entry* b = pb; + int r = jv_cmp(jv_copy(a->key), jv_copy(b->key)); ++ if (r == INT_MIN) { ++ sort_cmp_state.too_deep = 1; ++ return 0; ++ } + // comparing by index if r == 0 makes the sort stable + return r ? r : (a->index - b->index); + } + +-static struct sort_entry* sort_items(jv objects, jv keys) { ++static struct sort_entry* sort_items(jv objects, jv keys, int *too_deep) { + assert(jv_get_kind(objects) == JV_KIND_ARRAY); + assert(jv_get_kind(keys) == JV_KIND_ARRAY); + assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); + int n = jv_array_length(jv_copy(objects)); ++ *too_deep = 0; + if (n == 0) { + jv_free(objects); + jv_free(keys); +@@ -713,7 +757,13 @@ static struct sort_entry* sort_items(jv objects, jv keys) { + } + jv_free(objects); + jv_free(keys); ++ sort_cmp_state.too_deep = 0; + qsort(entries, n, sizeof(struct sort_entry), sort_cmp); ++ if (sort_cmp_state.too_deep) { ++ sort_entry_array_free(entries, 0, n); ++ *too_deep = 1; ++ return NULL; ++ } + return entries; + } + +@@ -722,7 +772,10 @@ jv jv_sort(jv objects, jv keys) { + assert(jv_get_kind(keys) == JV_KIND_ARRAY); + assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); + int n = jv_array_length(jv_copy(objects)); +- struct sort_entry* entries = sort_items(objects, keys); ++ int too_deep = 0; ++ struct sort_entry* entries = sort_items(objects, keys, &too_deep); ++ if (too_deep) ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); + jv ret = jv_array(); + for (int i=0; i<n; i++) { + jv_free(entries[i].key); +@@ -737,13 +790,24 @@ jv jv_group(jv objects, jv keys) { + assert(jv_get_kind(keys) == JV_KIND_ARRAY); + assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); + int n = jv_array_length(jv_copy(objects)); +- struct sort_entry* entries = sort_items(objects, keys); ++ int too_deep = 0; ++ struct sort_entry* entries = sort_items(objects, keys, &too_deep); ++ if (too_deep) ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); + jv ret = jv_array(); + if (n > 0) { + jv curr_key = entries[0].key; + jv group = jv_array_append(jv_array(), entries[0].object); + for (int i = 1; i < n; i++) { +- if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) { ++ int equal = jv_equal(jv_copy(curr_key), jv_copy(entries[i].key)); ++ if (equal < 0) { ++ jv_free(curr_key); ++ jv_free(group); ++ sort_entry_array_free(entries, i, n); ++ jv_free(ret); ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ } ++ if (equal) { + jv_free(entries[i].key); + } else { + jv_free(curr_key); +@@ -765,11 +829,21 @@ jv jv_unique(jv objects, jv keys) { + assert(jv_get_kind(keys) == JV_KIND_ARRAY); + assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); + int n = jv_array_length(jv_copy(objects)); +- struct sort_entry* entries = sort_items(objects, keys); ++ int too_deep = 0; ++ struct sort_entry* entries = sort_items(objects, keys, &too_deep); ++ if (too_deep) ++ return jv_invalid_with_msg(jv_string("Comparison too deep")); + jv ret = jv_array(); + jv curr_key = jv_invalid(); + for (int i = 0; i < n; i++) { +- if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) { ++ int equal = jv_equal(jv_copy(curr_key), jv_copy(entries[i].key)); ++ if (equal < 0) { ++ jv_free(curr_key); ++ sort_entry_array_free(entries, i, n); ++ jv_free(ret); ++ return jv_invalid_with_msg(jv_string("Equality check too deep")); ++ } ++ if (equal) { + jv_free(entries[i].key); + jv_free(entries[i].object); + } else { diff --git a/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch b/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch new file mode 100644 index 0000000000..c1b0dadfdb --- /dev/null +++ b/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch @@ -0,0 +1,32 @@ +From e987df0d463d85fd70825e042a082427e8275b86 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Mon, 8 Jun 2026 22:14:48 +0900 +Subject: [PATCH] Fix heap-buffer-overflow in raw file loading + +When `jv_string_append_buf` overflows the string length limit, +it returns an invalid `jv`; `jv_load_file` then re-entered it +on the invalid value and overran the heap. Break out of the loop +once the value is invalid. + +Fixes CVE-2026-49839. + +CVE: CVE-2026-49839 +Upstream: https://github.com/jqlang/jq/commit/e987df0d463d85fd70825e042a082427e8275b86 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/jv_file.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/jv_file.c b/src/jv_file.c +index 7706b0e06e..fbc1e4d653 100644 +--- a/src/jv_file.c ++++ b/src/jv_file.c +@@ -57,6 +57,8 @@ jv jv_load_file(const char* filename, int raw) { + + if (raw) { + data = jv_string_append_buf(data, buf, n); ++ if (!jv_is_valid(data)) ++ break; + } else { + jv_parser_set_buf(parser, buf, n, !feof(file)); + jv value; diff --git a/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch b/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch new file mode 100644 index 0000000000..606c818a41 --- /dev/null +++ b/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch @@ -0,0 +1,73 @@ +From 46d1da30944ce93dd671ac72b6513fc0eb747837 Mon Sep 17 00:00:00 2001 +From: itchyny <itchyny@cybozu.co.jp> +Date: Tue, 16 Jun 2026 14:31:14 +0900 +Subject: [PATCH] Tighten string length bounds and propagate invalid jv in + implode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bound added in CVE-2026-32316 (e47e56d22) still allowed +`sizeof(jvp_string) + (currlen + len) * 2 + 1` to wrap `size_t` on +32-bit platforms. Tighten the threshold so the final allocation +fits in 32-bit `size_t`. + +Also break out of `jv_string_implode` and `f_string_implode` once +`jv_string_append_codepoint` returns an invalid `jv`; otherwise the +next iteration triggers the assertion in `jvp_string_ptr` (or +invokes undefined behavior under `-DNDEBUG`). + +Fixes CVE-2026-54679. + +Co-authored-by: Dirk Müller <dirk@dmllr.de> + +CVE: CVE-2026-54679 +Upstream: https://github.com/jqlang/jq/commit/46d1da30944ce93dd671ac72b6513fc0eb747837 +Signed-off-by: Thomas Perale <thomas.perale@mind.be> +--- + src/builtin.c | 1 + + src/jv.c | 5 +++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/builtin.c b/src/builtin.c +index ed205d9935..a3b7a61ae8 100644 +--- a/src/builtin.c ++++ b/src/builtin.c +@@ -1396,6 +1396,7 @@ static jv f_string_implode(jq_state *jq, jv a) { + if (nv < 0 || nv > 0x10FFFF || (nv >= 0xD800 && nv <= 0xDFFF)) + nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER + s = jv_string_append_codepoint(s, nv); ++ if (!jv_is_valid(s)) break; + } + + jv_free(a); +diff --git a/src/jv.c b/src/jv.c +index bcd7c9b5b0..48a63e6e55 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1197,7 +1197,7 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) { + static jv jvp_string_append(jv string, const char* data, uint32_t len) { + jvp_string* s = jvp_string_ptr(string); + uint32_t currlen = jvp_string_length(s); +- if ((uint64_t)currlen + len >= INT_MAX) { ++ if ((uint64_t)currlen + len >= INT_MAX - sizeof(jvp_string) / 2) { + jv_free(string); + return jv_invalid_with_msg(jv_string("String too long")); + } +@@ -1398,7 +1398,7 @@ jv jv_string_repeat(jv j, int n) { + } + int len = jv_string_length_bytes(jv_copy(j)); + int64_t res_len = (int64_t)len * n; +- if (res_len >= INT_MAX) { ++ if ((uint64_t)res_len >= INT_MAX - sizeof(jvp_string) / 2) { + jv_free(j); + return jv_invalid_with_msg(jv_string("Repeat string result too long")); + } +@@ -1483,6 +1483,7 @@ jv jv_string_implode(jv j) { + if (nv < 0 || nv > 0x10FFFF || (nv >= 0xD800 && nv <= 0xDFFF)) + nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER + s = jv_string_append_codepoint(s, nv); ++ if (!jv_is_valid(s)) break; + } + + jv_free(j); diff --git a/package/jq/jq.mk b/package/jq/jq.mk index 19a130b694..eaa04846e0 100644 --- a/package/jq/jq.mk +++ b/package/jq/jq.mk @@ -12,6 +12,51 @@ JQ_LICENSE_FILES = COPYING JQ_CPE_ID_VENDOR = jqlang JQ_INSTALL_STAGING = YES +# 0001-fix-out-of-bounds-read-in-jv-parse-sized.patch +JQ_IGNORE_CVES += CVE-2026-39979 + +# 0002-fix-NUL-truncation-in-the-JSON-parser.patch +JQ_IGNORE_CVES += CVE-2026-33948 + +# 0003-limit-path-depth-to-prevent-stack-overflow.patch +JQ_IGNORE_CVES += CVE-2026-33947 + +# 0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch +JQ_IGNORE_CVES += CVE-2026-32316 + +# 0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch +JQ_IGNORE_CVES += CVE-2026-40164 + +# 0006-limit-the-containment-check-depth.patch +JQ_IGNORE_CVES += 8:CVE: CVE-2026-40612 + +# 0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch +JQ_IGNORE_CVES += CVE-2026-41256 + +# 0008-fix-signed-int-overflow-in-stack-reallocate.patch +JQ_IGNORE_CVES += CVE-2026-41257 + +# 0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch +JQ_IGNORE_CVES += CVE-2026-43894 + +# 0010-reject-embedded-NUL-bytes-in-module-import-paths.patch +JQ_IGNORE_CVES += CVE-2026-43895 + +# 0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch +JQ_IGNORE_CVES += CVE-2026-43896 + +# 0012-detect-circular-module-imports-to-prevent-stack-overflow.patch +JQ_IGNORE_CVES += CVE-2026-44777 + +# 0013-guard-deep-structural-equality-and-comparison-recursion.patch +JQ_IGNORE_CVES += CVE-2026-47770 + +# 0014-fix-heap-buffer-overflow-in-raw-file-loading.patch +JQ_IGNORE_CVES += CVE-2026-49839 + +# 0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch +JQ_IGNORE_CVES += CVE-2026-54679 + # uses c99 specific features JQ_CONF_ENV += CFLAGS="$(TARGET_CFLAGS) -std=c99" HOST_JQ_CONF_ENV += CFLAGS="$(HOST_CFLAGS) -std=c99" -- 2.54.0 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 2026-06-30 20:15 [Buildroot] [PATCH 1/2] package/jq: patch various CVEs Thomas Perale via buildroot @ 2026-06-30 20:15 ` Thomas Perale via buildroot 2026-07-02 6:44 ` Peter Korsgaard 0 siblings, 1 reply; 4+ messages in thread From: Thomas Perale via buildroot @ 2026-06-30 20:15 UTC (permalink / raw) To: buildroot; +Cc: Angelo Compagnucci, Danomi Manchego For more information about the release, see: - https://github.com/jqlang/jq/releases/tag/jq-1.8.2 All the CVEs were addressed by a patch but non-CVEs security fixes such as GHSA-gf4g-95wj-4q4r or GHSA-hj52-j2c9-r8r4 are fixed by this release. Signed-off-by: Thomas Perale <thomas.perale@mind.be> --- ...out-of-bounds-read-in-jv-parse-sized.patch | 30 - ...ix-NUL-truncation-in-the-JSON-parser.patch | 35 - ...path-depth-to-prevent-stack-overflow.patch | 72 - ...pend-and-jvp-string-copy-replace-bad.patch | 53 - ...-mitigate-hash-collision-DoS-attacks.patch | 90 - ...06-limit-the-containment-check-depth.patch | 120 -- ...ation-in-program-files-loaded-with-f.patch | 34 - ...ned-int-overflow-in-stack-reallocate.patch | 53 - ...-literals-longer-than-DEC-MAX-DIGITS.patch | 53 - ...ded-NUL-bytes-in-module-import-paths.patch | 1512 ----------------- ...erge-depth-to-prevent-stack-overflow.patch | 66 - ...le-imports-to-prevent-stack-overflow.patch | 142 -- ...al-equality-and-comparison-recursion.patch | 423 ----- ...-buffer-overflow-in-raw-file-loading.patch | 32 - ...-and-propagate-invalid-jv-in-implode.patch | 73 - package/jq/jq.hash | 2 +- package/jq/jq.mk | 47 +- 17 files changed, 2 insertions(+), 2835 deletions(-) delete mode 100644 package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch delete mode 100644 package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch delete mode 100644 package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch delete mode 100644 package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch delete mode 100644 package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch delete mode 100644 package/jq/0006-limit-the-containment-check-depth.patch delete mode 100644 package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch delete mode 100644 package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch delete mode 100644 package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch delete mode 100644 package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch delete mode 100644 package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch delete mode 100644 package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch delete mode 100644 package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch delete mode 100644 package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch delete mode 100644 package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch diff --git a/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch b/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch deleted file mode 100644 index 77fe358e7e..0000000000 --- a/package/jq/0001-fix-out-of-bounds-read-in-jv-parse-sized.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 2f09060afab23fe9390cce7cb860b10416e1bf5f Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Mon, 13 Apr 2026 11:04:52 +0900 -Subject: [PATCH] Fix out-of-bounds read in jv_parse_sized() - -This fixes CVE-2026-39979. - -Co-authored-by: Mattias Wadman <mattias.wadman@gmail.com> -Upstream: https://github.com/jqlang/jq/commit/2f09060afab23fe9390cce7cb860b10416e1bf5f -CVE: CVE-2026-39979 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv_parse.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/jv_parse.c b/src/jv_parse.c -index aa2054cc09..56847b5eaa 100644 ---- a/src/jv_parse.c -+++ b/src/jv_parse.c -@@ -892,8 +892,9 @@ jv jv_parse_sized_custom_flags(const char* string, int length, int flags) { - - if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) { - jv msg = jv_invalid_get_msg(value); -- value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", -+ value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%.*s')", - jv_string_value(msg), -+ length, - string)); - jv_free(msg); - } diff --git a/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch b/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch deleted file mode 100644 index 90d26fbd34..0000000000 --- a/package/jq/0002-fix-NUL-truncation-in-the-JSON-parser.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Mon, 13 Apr 2026 08:46:11 +0900 -Subject: [PATCH] Fix NUL truncation in the JSON parser - -This fixes CVE-2026-33948. - -Upstream: https://github.com/jqlang/jq/commit/6374ae0bcdfe33a18eb0ae6db28493b1f34a0a5b -CVE: CVE-2026-33948 -[thomas: remove tests] -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/util.c | 8 +------- - tests/shtest | 6 ++++++ - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/src/util.c b/src/util.c -index fdfdb96d88..80d65fc808 100644 ---- a/src/util.c -+++ b/src/util.c -@@ -309,13 +309,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) { - if (p != NULL) - state->current_line++; - -- if (p == NULL && state->parser != NULL) { -- /* -- * There should be no NULs in JSON texts (but JSON text -- * sequences are another story). -- */ -- state->buf_valid_len = strlen(state->buf); -- } else if (p == NULL && feof(state->current_input)) { -+ if (p == NULL && feof(state->current_input)) { - size_t i; - - /* diff --git a/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch b/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch deleted file mode 100644 index 498d4b81f6..0000000000 --- a/package/jq/0003-limit-path-depth-to-prevent-stack-overflow.patch +++ /dev/null @@ -1,72 +0,0 @@ -From fb59f1491058d58bdc3e8dd28f1773d1ac690a1f Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Mon, 13 Apr 2026 11:23:40 +0900 -Subject: [PATCH] Limit path depth to prevent stack overflow - -Deeply nested path arrays can cause unbounded recursion in -`jv_setpath`, `jv_getpath`, and `jv_delpaths`, leading to -stack overflow. Add a depth limit of 10000 to match the -existing `tojson` depth limit. This fixes CVE-2026-33947. - -CVE: CVE-2026-33947 -Upstream: https://github.com/jqlang/jq/commit/fb59f1491058d58bdc3e8dd28f1773d1ac690a1f -[thomas: remove tests] -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv_aux.c | 21 +++++++++++++++++++++ - tests/jq.test | 25 +++++++++++++++++++++++++ - 2 files changed, 46 insertions(+) - -diff --git a/src/jv_aux.c b/src/jv_aux.c -index 018f380b10..fd5ff96684 100644 ---- a/src/jv_aux.c -+++ b/src/jv_aux.c -@@ -375,6 +375,10 @@ static jv jv_dels(jv t, jv keys) { - return t; - } - -+#ifndef MAX_PATH_DEPTH -+#define MAX_PATH_DEPTH (10000) -+#endif -+ - jv jv_setpath(jv root, jv path, jv value) { - if (jv_get_kind(path) != JV_KIND_ARRAY) { - jv_free(value); -@@ -382,6 +386,12 @@ jv jv_setpath(jv root, jv path, jv value) { - jv_free(path); - return jv_invalid_with_msg(jv_string("Path must be specified as an array")); - } -+ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { -+ jv_free(value); -+ jv_free(root); -+ jv_free(path); -+ return jv_invalid_with_msg(jv_string("Path too deep")); -+ } - if (!jv_is_valid(root)){ - jv_free(value); - jv_free(path); -@@ -434,6 +444,11 @@ jv jv_getpath(jv root, jv path) { - jv_free(path); - return jv_invalid_with_msg(jv_string("Path must be specified as an array")); - } -+ if (jv_array_length(jv_copy(path)) > MAX_PATH_DEPTH) { -+ jv_free(root); -+ jv_free(path); -+ return jv_invalid_with_msg(jv_string("Path too deep")); -+ } - if (!jv_is_valid(root)) { - jv_free(path); - return root; -@@ -511,6 +526,12 @@ jv jv_delpaths(jv object, jv paths) { - jv_free(elem); - return err; - } -+ if (jv_array_length(jv_copy(elem)) > MAX_PATH_DEPTH) { -+ jv_free(object); -+ jv_free(paths); -+ jv_free(elem); -+ return jv_invalid_with_msg(jv_string("Path too deep")); -+ } - jv_free(elem); - } - if (jv_array_length(jv_copy(paths)) == 0) { diff --git a/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch b/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch deleted file mode 100644 index b835c9d1c8..0000000000 --- a/package/jq/0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch +++ /dev/null @@ -1,53 +0,0 @@ -From e47e56d226519635768e6aab2f38f0ab037c09e5 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Thu, 12 Mar 2026 20:28:43 +0900 -Subject: [PATCH] Fix heap buffer overflow in `jvp_string_append` and - `jvp_string_copy_replace_bad` - -In `jvp_string_append`, the allocation size `(currlen + len) * 2` could -overflow `uint32_t` when `currlen + len` exceeds `INT_MAX`, causing a small -allocation followed by a large `memcpy`. - -In `jvp_string_copy_replace_bad`, the output buffer size calculation -`length * 3 + 1` could overflow `uint32_t`, again resulting in a small -allocation followed by a large write. - -Add overflow checks to both functions to return an error for strings -that would exceed `INT_MAX` in length. Fixes CVE-2026-32316. - -CVE: CVE-2026-32316 -Upstream: https://github.com/jqlang/jq/commit/e47e56d226519635768e6aab2f38f0ab037c09e5 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/src/jv.c b/src/jv.c -index 722a539391..2a62b48419 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -1114,7 +1114,12 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { - const char* end = data + length; - const char* i = data; - -- uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD -+ // worst case: all bad bytes, each becomes a 3-byte U+FFFD -+ uint64_t maxlength = (uint64_t)length * 3 + 1; -+ if (maxlength >= INT_MAX) { -+ return jv_invalid_with_msg(jv_string("String too long")); -+ } -+ - jvp_string* s = jvp_string_alloc(maxlength); - char* out = s->data; - int c = 0; -@@ -1174,6 +1179,10 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) { - static jv jvp_string_append(jv string, const char* data, uint32_t len) { - jvp_string* s = jvp_string_ptr(string); - uint32_t currlen = jvp_string_length(s); -+ if ((uint64_t)currlen + len >= INT_MAX) { -+ jv_free(string); -+ return jv_invalid_with_msg(jv_string("String too long")); -+ } - - if (jvp_refcnt_unshared(string.u.ptr) && - jvp_string_remaining_space(s) >= len) { diff --git a/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch b/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch deleted file mode 100644 index ac4d6c445e..0000000000 --- a/package/jq/0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Mon, 13 Apr 2026 08:53:26 +0900 -Subject: [PATCH] Randomize hash seed to mitigate hash collision DoS attacks - -The hash function used a fixed seed, allowing attackers to craft colliding keys -and cause O(n^2) object parsing performance. Initialize the seed from a random -source at process startup to prevent the attack. This fixes CVE-2026-40164. - -Co-authored-by: Asaf Meizner <asafmeizner@gmail.com> -CVE: CVE-2026-40164 -Upstream: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - configure.ac | 2 ++ - src/jv.c | 34 ++++++++++++++++++++++++++++++++-- - 2 files changed, 34 insertions(+), 2 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 5dac42655a..f7067a4341 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -143,6 +143,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define - AC_CHECK_MEMBER([struct tm.__tm_gmtoff], [AC_DEFINE([HAVE_TM___TM_GMT_OFF],1,[Define to 1 if the system has the __tm_gmt_off field in struct tm])], - [], [[#include <time.h>]]) - AC_FIND_FUNC([setlocale], [c], [#include <locale.h>], [0,0]) -+AC_FIND_FUNC([arc4random], [c], [#include <stdlib.h>], []) -+AC_FIND_FUNC([getentropy], [c], [#include <unistd.h>], [0, 0]) - - dnl Figure out if we have the pthread functions we actually need - AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include <pthread.h>], [NULL, NULL]) -diff --git a/src/jv.c b/src/jv.c -index 2a62b48419..607ac174f7 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -40,6 +40,10 @@ - #include <limits.h> - #include <math.h> - #include <float.h> -+#include <time.h> -+#include <unistd.h> -+#include <fcntl.h> -+#include <pthread.h> - - #include "jv_alloc.h" - #include "jv.h" -@@ -1206,7 +1210,33 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { - } - } - --static const uint32_t HASH_SEED = 0x432A9843; -+static uint32_t hash_seed; -+static pthread_once_t hash_seed_once = PTHREAD_ONCE_INIT; -+ -+static void jvp_hash_seed_init(void) { -+ uint32_t seed; -+#if defined(HAVE_ARC4RANDOM) -+ seed = arc4random(); -+#elif defined(HAVE_GETENTROPY) -+ if (getentropy(&seed, sizeof(seed)) != 0) -+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); -+#else -+ int fd = open("/dev/urandom", O_RDONLY); -+ if (fd >= 0) { -+ if (read(fd, &seed, sizeof(seed)) != 4) -+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); -+ close(fd); -+ } else { -+ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); -+ } -+#endif -+ hash_seed = seed; -+} -+ -+static uint32_t jvp_hash_seed(void) { -+ pthread_once(&hash_seed_once, jvp_hash_seed_init); -+ return hash_seed; -+} - - static uint32_t rotl32 (uint32_t x, int8_t r){ - return (x << r) | (x >> (32 - r)); -@@ -1225,7 +1255,7 @@ static uint32_t jvp_string_hash(jv jstr) { - int len = (int)jvp_string_length(str); - const int nblocks = len / 4; - -- uint32_t h1 = HASH_SEED; -+ uint32_t h1 = jvp_hash_seed(); - - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; diff --git a/package/jq/0006-limit-the-containment-check-depth.patch b/package/jq/0006-limit-the-containment-check-depth.patch deleted file mode 100644 index 7834b8ed23..0000000000 --- a/package/jq/0006-limit-the-containment-check-depth.patch +++ /dev/null @@ -1,120 +0,0 @@ -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. - -CVE: CVE-2026-40612 -Upstream: https://github.com/jqlang/jq/commit/d1a12569d91641135976a8536776a4a329c02cc2 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - 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 d33e9fb162..2b2a2d40da 100644 ---- a/src/builtin.c -+++ b/src/builtin.c -@@ -419,7 +419,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 607ac174f7..4b18c00cf6 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -938,19 +938,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; - } - } -@@ -1843,7 +1843,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; -@@ -1851,9 +1851,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; - } -@@ -2084,14 +2084,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) { -@@ -2107,3 +2116,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/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch b/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch deleted file mode 100644 index f67a7c8ed4..0000000000 --- a/package/jq/0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 5a015deae35d19e3ebbc65db6c157a80e76df738 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. - -CVE: CVE-2026-41256 -Upstream: https://github.com/jqlang/jq/commit/5a015deae35d19e3ebbc65db6c157a80e76df738 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/main.c | 8 ++++++++ - tests/shtest | 7 +++++++ - 2 files changed, 15 insertions(+) - -diff --git a/src/main.c b/src/main.c -index ce362607e2..fb5c7ab8e3 100644 ---- a/src/main.c -+++ b/src/main.c -@@ -611,6 +611,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/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch b/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch deleted file mode 100644 index b0c39ed23b..0000000000 --- a/package/jq/0008-fix-signed-int-overflow-in-stack-reallocate.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 01b3cded76daacbfddb7f8763700b0803bcb5c6f 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. - -CVE: CVE-2026-41257 -Upstream: https://github.com/jqlang/jq/commit/01b3cded76daacbfddb7f8763700b0803bcb5c6f -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - 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 2a063e8cf9..159c56e4fb 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) { diff --git a/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch b/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch deleted file mode 100644 index e1004d4707..0000000000 --- a/package/jq/0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Wed, 6 May 2026 19:45:24 +0900 -Subject: [PATCH] Reject numeric literals longer than DEC_MAX_DIGITS - (999999999) - -A signed-int overflow in decNumber's D2U macro lets huge literals -write attacker-controlled bytes past a stack buffer. Cap the length -before calling decNumberFromString, and pre-slice long strings in -jv_dump_string_trunc so the resulting error message doesn't itself -allocate a multi-GiB buffer. - -Fixes CVE-2026-43894. - -CVE: CVE-2026-43894 -Upstream: https://github.com/jqlang/jq/commit/9761ceb7d6cc48c16b25f0ab1baaef0e701927e4 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv.c | 5 ++++- - src/jv_print.c | 4 ++++ - 2 files changed, 8 insertions(+), 1 deletion(-) - -diff --git a/src/jv.c b/src/jv.c -index 84fafef666..074ee310c5 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -578,7 +578,10 @@ static jvp_literal_number* jvp_literal_number_alloc(unsigned literal_length) { - } - - static jv jvp_literal_number_new(const char * literal) { -- jvp_literal_number* n = jvp_literal_number_alloc(strlen(literal)); -+ size_t len = strlen(literal); -+ if (len > DEC_MAX_DIGITS) -+ return JV_INVALID; -+ jvp_literal_number* n = jvp_literal_number_alloc(len); - - decContext *ctx = DEC_CONTEXT(); - decContextClearStatus(ctx, DEC_Conversion_syntax); -diff --git a/src/jv_print.c b/src/jv_print.c -index 5c86c5d97c..bc251070f7 100644 ---- a/src/jv_print.c -+++ b/src/jv_print.c -@@ -408,6 +408,10 @@ jv jv_dump_string(jv x, int flags) { - } - - char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) { -+ if (jv_get_kind(x) == JV_KIND_STRING && -+ (size_t)jv_string_length_bytes(jv_copy(x)) > bufsize) { -+ x = jv_string_slice(x, 0, bufsize); -+ } - x = jv_dump_string(x, 0); - const char *str = jv_string_value(x); - const size_t len = strlen(str); diff --git a/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch b/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch deleted file mode 100644 index 11824a2d44..0000000000 --- a/package/jq/0010-reject-embedded-NUL-bytes-in-module-import-paths.patch +++ /dev/null @@ -1,1512 +0,0 @@ -From 9d223f153c3632a207fa071caaa6292da33ae361 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Sat, 9 May 2026 17:08:43 +0900 -Subject: [PATCH] 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: https://github.com/jqlang/jq/commit/9d223f153c3632a207fa071caaa6292da33ae361 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/compile.c | 12 +- - src/compile.h | 2 +- - src/linker.c | 6 +- - src/parser.c | 556 +++++++++++++++++++++++++------------------------- - src/parser.y | 16 +- - tests/shtest | 17 ++ - 6 files changed, 307 insertions(+), 302 deletions(-) - -diff --git a/src/compile.c b/src/compile.c -index 5e64946b3e..80b723c119 100644 ---- a/src/compile.c -+++ b/src/compile.c -@@ -525,13 +525,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 bef46328a7..d195e9e2e8 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 cfd74d1d48..e9027004cc 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) { - jv home = get_home(); - if (jv_is_valid(home)) { - /* Import ~/.jq as a library named "" found in $HOME or %USERPROFILE% */ -- 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"), home))); -diff --git a/src/parser.c b/src/parser.c -index c90e313420..9c60173e27 100644 ---- a/src/parser.c -+++ b/src/parser.c -@@ -937,19 +937,19 @@ static const yytype_int16 yyrline[] = - 325, 328, 331, 337, 340, 343, 349, 352, 355, 358, - 361, 364, 367, 370, 373, 376, 379, 382, 385, 388, - 391, 394, 397, 400, 403, 406, 409, 412, 415, 421, -- 424, 441, 450, 457, 465, 476, 481, 487, 490, 495, -- 499, 506, 509, 515, 522, 525, 528, 534, 537, 540, -- 546, 549, 552, 560, 564, 567, 570, 573, 576, 579, -- 582, 585, 588, 592, 598, 601, 604, 607, 610, 613, -- 616, 619, 622, 625, 628, 631, 634, 637, 640, 643, -- 646, 649, 652, 655, 658, 661, 664, 671, 674, 677, -- 680, 683, 687, 690, 694, 712, 716, 720, 723, 735, -- 740, 741, 742, 743, 746, 749, 754, 759, 762, 767, -- 770, 775, 779, 782, 787, 790, 795, 798, 803, 806, -- 809, 812, 815, 818, 826, 832, 835, 838, 841, 844, -- 847, 850, 853, 856, 859, 862, 865, 868, 871, 874, -- 877, 880, 883, 889, 892, 895, 900, 903, 906, 909, -- 913, 918, 922, 926, 930, 934, 942, 948, 951 -+ 424, 441, 445, 449, 455, 466, 471, 477, 480, 485, -+ 489, 496, 499, 505, 512, 515, 518, 524, 527, 530, -+ 536, 539, 542, 550, 554, 557, 560, 563, 566, 569, -+ 572, 575, 578, 582, 588, 591, 594, 597, 600, 603, -+ 606, 609, 612, 615, 618, 621, 624, 627, 630, 633, -+ 636, 639, 642, 645, 648, 651, 654, 661, 664, 667, -+ 670, 673, 677, 680, 684, 702, 706, 710, 713, 725, -+ 730, 731, 732, 733, 736, 739, 744, 749, 752, 757, -+ 760, 765, 769, 772, 777, 780, 785, 788, 793, 796, -+ 799, 802, 805, 808, 816, 822, 825, 828, 831, 834, -+ 837, 840, 843, 846, 849, 852, 855, 858, 861, 864, -+ 867, 870, 873, 879, 882, 885, 890, 893, 896, 899, -+ 903, 908, 912, 916, 920, 924, 932, 938, 941 - }; - #endif - -@@ -2841,42 +2841,32 @@ YYLTYPE yylloc = yyloc_default; - case 41: /* ImportWhat: "import" ImportFrom "as" BINDING */ - #line 441 "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 2853 "src/parser.c" -+#line 2848 "src/parser.c" - break; - - case 42: /* ImportWhat: "import" ImportFrom "as" IDENT */ --#line 450 "src/parser.y" -+#line 445 "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 2865 "src/parser.c" -+#line 2857 "src/parser.c" - break; - - case 43: /* ImportWhat: "include" ImportFrom */ --#line 457 "src/parser.y" -+#line 449 "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 2876 "src/parser.c" -+#line 2866 "src/parser.c" - break; - - case 44: /* ImportFrom: String */ --#line 465 "src/parser.y" -+#line 455 "src/parser.y" - { - if (!block_is_const((yyvsp[0].blk))) { - FAIL((yylsp[0]), "Import path must be constant"); -@@ -2886,152 +2876,152 @@ YYLTYPE yylloc = yyloc_default; - (yyval.blk) = (yyvsp[0].blk); - } - } --#line 2890 "src/parser.c" -+#line 2880 "src/parser.c" - break; - - case 45: /* FuncDef: "def" IDENT ':' Query ';' */ --#line 476 "src/parser.y" -+#line 466 "src/parser.y" - { - (yyval.blk) = gen_function(jv_string_value((yyvsp[-3].literal)), gen_noop(), (yyvsp[-1].blk)); - jv_free((yyvsp[-3].literal)); - } --#line 2899 "src/parser.c" -+#line 2889 "src/parser.c" - break; - - case 46: /* FuncDef: "def" IDENT '(' Params ')' ':' Query ';' */ --#line 481 "src/parser.y" -+#line 471 "src/parser.y" - { - (yyval.blk) = gen_function(jv_string_value((yyvsp[-6].literal)), (yyvsp[-4].blk), (yyvsp[-1].blk)); - jv_free((yyvsp[-6].literal)); - } --#line 2908 "src/parser.c" -+#line 2898 "src/parser.c" - break; - - case 47: /* Params: Param */ --#line 487 "src/parser.y" -+#line 477 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 2916 "src/parser.c" -+#line 2906 "src/parser.c" - break; - - case 48: /* Params: Params ';' Param */ --#line 490 "src/parser.y" -+#line 480 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 2924 "src/parser.c" -+#line 2914 "src/parser.c" - break; - - case 49: /* Param: BINDING */ --#line 495 "src/parser.y" -+#line 485 "src/parser.y" - { - (yyval.blk) = gen_param_regular(jv_string_value((yyvsp[0].literal))); - jv_free((yyvsp[0].literal)); - } --#line 2933 "src/parser.c" -+#line 2923 "src/parser.c" - break; - - case 50: /* Param: IDENT */ --#line 499 "src/parser.y" -+#line 489 "src/parser.y" - { - (yyval.blk) = gen_param(jv_string_value((yyvsp[0].literal))); - jv_free((yyvsp[0].literal)); - } --#line 2942 "src/parser.c" -+#line 2932 "src/parser.c" - break; - - case 51: /* StringStart: FORMAT QQSTRING_START */ --#line 506 "src/parser.y" -+#line 496 "src/parser.y" - { - (yyval.literal) = (yyvsp[-1].literal); - } --#line 2950 "src/parser.c" -+#line 2940 "src/parser.c" - break; - - case 52: /* StringStart: QQSTRING_START */ --#line 509 "src/parser.y" -+#line 499 "src/parser.y" - { - (yyval.literal) = jv_string("text"); - } --#line 2958 "src/parser.c" -+#line 2948 "src/parser.c" - break; - - case 53: /* String: StringStart QQString QQSTRING_END */ --#line 515 "src/parser.y" -+#line 505 "src/parser.y" - { - (yyval.blk) = (yyvsp[-1].blk); - jv_free((yyvsp[-2].literal)); - } --#line 2967 "src/parser.c" -+#line 2957 "src/parser.c" - break; - - case 54: /* QQString: %empty */ --#line 522 "src/parser.y" -+#line 512 "src/parser.y" - { - (yyval.blk) = gen_const(jv_string("")); - } --#line 2975 "src/parser.c" -+#line 2965 "src/parser.c" - break; - - case 55: /* QQString: QQString QQSTRING_TEXT */ --#line 525 "src/parser.y" -+#line 515 "src/parser.y" - { - (yyval.blk) = gen_binop((yyvsp[-1].blk), gen_const((yyvsp[0].literal)), '+'); - } --#line 2983 "src/parser.c" -+#line 2973 "src/parser.c" - break; - - case 56: /* QQString: QQString QQSTRING_INTERP_START Query QQSTRING_INTERP_END */ --#line 528 "src/parser.y" -+#line 518 "src/parser.y" - { - (yyval.blk) = gen_binop((yyvsp[-3].blk), gen_format((yyvsp[-1].blk), jv_copy((yyvsp[-4].literal))), '+'); - } --#line 2991 "src/parser.c" -+#line 2981 "src/parser.c" - break; - - case 57: /* ElseBody: "elif" Query "then" Query ElseBody */ --#line 534 "src/parser.y" -+#line 524 "src/parser.y" - { - (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); - } --#line 2999 "src/parser.c" -+#line 2989 "src/parser.c" - break; - - case 58: /* ElseBody: "else" Query "end" */ --#line 537 "src/parser.y" -+#line 527 "src/parser.y" - { - (yyval.blk) = (yyvsp[-1].blk); - } --#line 3007 "src/parser.c" -+#line 2997 "src/parser.c" - break; - - case 59: /* ElseBody: "end" */ --#line 540 "src/parser.y" -+#line 530 "src/parser.y" - { - (yyval.blk) = gen_noop(); - } --#line 3015 "src/parser.c" -+#line 3005 "src/parser.c" - break; - - case 60: /* Term: '.' */ --#line 546 "src/parser.y" -+#line 536 "src/parser.y" - { - (yyval.blk) = gen_noop(); - } --#line 3023 "src/parser.c" -+#line 3013 "src/parser.c" - break; - - case 61: /* Term: ".." */ --#line 549 "src/parser.y" -+#line 539 "src/parser.y" - { - (yyval.blk) = gen_call("recurse", gen_noop()); - } --#line 3031 "src/parser.c" -+#line 3021 "src/parser.c" - break; - - case 62: /* Term: "break" BINDING */ --#line 552 "src/parser.y" -+#line 542 "src/parser.y" - { - jv v = jv_string_fmt("*label-%s", jv_string_value((yyvsp[0].literal))); // impossible symbol - (yyval.blk) = gen_location((yyloc), locations, -@@ -3040,279 +3030,279 @@ YYLTYPE yylloc = yyloc_default; - jv_free(v); - jv_free((yyvsp[0].literal)); - } --#line 3044 "src/parser.c" -+#line 3034 "src/parser.c" - break; - - case 63: /* Term: "break" error */ --#line 560 "src/parser.y" -+#line 550 "src/parser.y" - { - FAIL((yyloc), "break requires a label to break to"); - (yyval.blk) = gen_noop(); - } --#line 3053 "src/parser.c" -+#line 3043 "src/parser.c" - break; - - case 64: /* Term: Term FIELD '?' */ --#line 564 "src/parser.y" -+#line 554 "src/parser.y" - { - (yyval.blk) = gen_index_opt((yyvsp[-2].blk), gen_const((yyvsp[-1].literal))); - } --#line 3061 "src/parser.c" -+#line 3051 "src/parser.c" - break; - - case 65: /* Term: FIELD '?' */ --#line 567 "src/parser.y" -+#line 557 "src/parser.y" - { - (yyval.blk) = gen_index_opt(gen_noop(), gen_const((yyvsp[-1].literal))); - } --#line 3069 "src/parser.c" -+#line 3059 "src/parser.c" - break; - - case 66: /* Term: Term '.' String '?' */ --#line 570 "src/parser.y" -+#line 560 "src/parser.y" - { - (yyval.blk) = gen_index_opt((yyvsp[-3].blk), (yyvsp[-1].blk)); - } --#line 3077 "src/parser.c" -+#line 3067 "src/parser.c" - break; - - case 67: /* Term: '.' String '?' */ --#line 573 "src/parser.y" -+#line 563 "src/parser.y" - { - (yyval.blk) = gen_index_opt(gen_noop(), (yyvsp[-1].blk)); - } --#line 3085 "src/parser.c" -+#line 3075 "src/parser.c" - break; - - case 68: /* Term: Term FIELD */ --#line 576 "src/parser.y" -+#line 566 "src/parser.y" - { - (yyval.blk) = gen_index((yyvsp[-1].blk), gen_const((yyvsp[0].literal))); - } --#line 3093 "src/parser.c" -+#line 3083 "src/parser.c" - break; - - case 69: /* Term: FIELD */ --#line 579 "src/parser.y" -+#line 569 "src/parser.y" - { - (yyval.blk) = gen_index(gen_noop(), gen_const((yyvsp[0].literal))); - } --#line 3101 "src/parser.c" -+#line 3091 "src/parser.c" - break; - - case 70: /* Term: Term '.' String */ --#line 582 "src/parser.y" -+#line 572 "src/parser.y" - { - (yyval.blk) = gen_index((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3109 "src/parser.c" -+#line 3099 "src/parser.c" - break; - - case 71: /* Term: '.' String */ --#line 585 "src/parser.y" -+#line 575 "src/parser.y" - { - (yyval.blk) = gen_index(gen_noop(), (yyvsp[0].blk)); - } --#line 3117 "src/parser.c" -+#line 3107 "src/parser.c" - break; - - case 72: /* Term: '.' error */ --#line 588 "src/parser.y" -+#line 578 "src/parser.y" - { - FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields"); - (yyval.blk) = gen_noop(); - } --#line 3126 "src/parser.c" -+#line 3116 "src/parser.c" - break; - - case 73: /* Term: '.' IDENT error */ --#line 592 "src/parser.y" -+#line 582 "src/parser.y" - { - jv_free((yyvsp[-1].literal)); - FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields"); - (yyval.blk) = gen_noop(); - } --#line 3136 "src/parser.c" -+#line 3126 "src/parser.c" - break; - - case 74: /* Term: Term '[' Query ']' '?' */ --#line 598 "src/parser.y" -+#line 588 "src/parser.y" - { - (yyval.blk) = gen_index_opt((yyvsp[-4].blk), (yyvsp[-2].blk)); - } --#line 3144 "src/parser.c" -+#line 3134 "src/parser.c" - break; - - case 75: /* Term: Term '[' Query ']' */ --#line 601 "src/parser.y" -+#line 591 "src/parser.y" - { - (yyval.blk) = gen_index((yyvsp[-3].blk), (yyvsp[-1].blk)); - } --#line 3152 "src/parser.c" -+#line 3142 "src/parser.c" - break; - - case 76: /* Term: Term '.' '[' Query ']' '?' */ --#line 604 "src/parser.y" -+#line 594 "src/parser.y" - { - (yyval.blk) = gen_index_opt((yyvsp[-5].blk), (yyvsp[-2].blk)); - } --#line 3160 "src/parser.c" -+#line 3150 "src/parser.c" - break; - - case 77: /* Term: Term '.' '[' Query ']' */ --#line 607 "src/parser.y" -+#line 597 "src/parser.y" - { - (yyval.blk) = gen_index((yyvsp[-4].blk), (yyvsp[-1].blk)); - } --#line 3168 "src/parser.c" -+#line 3158 "src/parser.c" - break; - - case 78: /* Term: Term '[' ']' '?' */ --#line 610 "src/parser.y" -+#line 600 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH_OPT)); - } --#line 3176 "src/parser.c" -+#line 3166 "src/parser.c" - break; - - case 79: /* Term: Term '[' ']' */ --#line 613 "src/parser.y" -+#line 603 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-2].blk), gen_op_simple(EACH)); - } --#line 3184 "src/parser.c" -+#line 3174 "src/parser.c" - break; - - case 80: /* Term: Term '.' '[' ']' '?' */ --#line 616 "src/parser.y" -+#line 606 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-4].blk), gen_op_simple(EACH_OPT)); - } --#line 3192 "src/parser.c" -+#line 3182 "src/parser.c" - break; - - case 81: /* Term: Term '.' '[' ']' */ --#line 619 "src/parser.y" -+#line 609 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH)); - } --#line 3200 "src/parser.c" -+#line 3190 "src/parser.c" - break; - - case 82: /* Term: Term '[' Query ':' Query ']' '?' */ --#line 622 "src/parser.y" -+#line 612 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-6].blk), (yyvsp[-4].blk), (yyvsp[-2].blk), INDEX_OPT); - } --#line 3208 "src/parser.c" -+#line 3198 "src/parser.c" - break; - - case 83: /* Term: Term '[' Query ':' ']' '?' */ --#line 625 "src/parser.y" -+#line 615 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), gen_const(jv_null()), INDEX_OPT); - } --#line 3216 "src/parser.c" -+#line 3206 "src/parser.c" - break; - - case 84: /* Term: Term '[' ':' Query ']' '?' */ --#line 628 "src/parser.y" -+#line 618 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-5].blk), gen_const(jv_null()), (yyvsp[-2].blk), INDEX_OPT); - } --#line 3224 "src/parser.c" -+#line 3214 "src/parser.c" - break; - - case 85: /* Term: Term '[' Query ':' Query ']' */ --#line 631 "src/parser.y" -+#line 621 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), INDEX); - } --#line 3232 "src/parser.c" -+#line 3222 "src/parser.c" - break; - - case 86: /* Term: Term '[' Query ':' ']' */ --#line 634 "src/parser.y" -+#line 624 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-4].blk), (yyvsp[-2].blk), gen_const(jv_null()), INDEX); - } --#line 3240 "src/parser.c" -+#line 3230 "src/parser.c" - break; - - case 87: /* Term: Term '[' ':' Query ']' */ --#line 637 "src/parser.y" -+#line 627 "src/parser.y" - { - (yyval.blk) = gen_slice_index((yyvsp[-4].blk), gen_const(jv_null()), (yyvsp[-1].blk), INDEX); - } --#line 3248 "src/parser.c" -+#line 3238 "src/parser.c" - break; - - case 88: /* Term: Term '?' */ --#line 640 "src/parser.y" -+#line 630 "src/parser.y" - { - (yyval.blk) = gen_try((yyvsp[-1].blk), gen_op_simple(BACKTRACK)); - } --#line 3256 "src/parser.c" -+#line 3246 "src/parser.c" - break; - - case 89: /* Term: LITERAL */ --#line 643 "src/parser.y" -+#line 633 "src/parser.y" - { - (yyval.blk) = gen_const((yyvsp[0].literal)); - } --#line 3264 "src/parser.c" -+#line 3254 "src/parser.c" - break; - - case 90: /* Term: String */ --#line 646 "src/parser.y" -+#line 636 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3272 "src/parser.c" -+#line 3262 "src/parser.c" - break; - - case 91: /* Term: FORMAT */ --#line 649 "src/parser.y" -+#line 639 "src/parser.y" - { - (yyval.blk) = gen_format(gen_noop(), (yyvsp[0].literal)); - } --#line 3280 "src/parser.c" -+#line 3270 "src/parser.c" - break; - - case 92: /* Term: '-' Term */ --#line 652 "src/parser.y" -+#line 642 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop())); - } --#line 3288 "src/parser.c" -+#line 3278 "src/parser.c" - break; - - case 93: /* Term: '(' Query ')' */ --#line 655 "src/parser.y" -+#line 645 "src/parser.y" - { - (yyval.blk) = (yyvsp[-1].blk); - } --#line 3296 "src/parser.c" -+#line 3286 "src/parser.c" - break; - - case 94: /* Term: '[' Query ']' */ --#line 658 "src/parser.y" -+#line 648 "src/parser.y" - { - (yyval.blk) = gen_collect((yyvsp[-1].blk)); - } --#line 3304 "src/parser.c" -+#line 3294 "src/parser.c" - break; - - case 95: /* Term: '[' ']' */ --#line 661 "src/parser.y" -+#line 651 "src/parser.y" - { - (yyval.blk) = gen_const(jv_array()); - } --#line 3312 "src/parser.c" -+#line 3302 "src/parser.c" - break; - - case 96: /* Term: '{' DictPairs '}' */ --#line 664 "src/parser.y" -+#line 654 "src/parser.y" - { - block o = gen_const_object((yyvsp[-1].blk)); - if (o.first != NULL) -@@ -3320,103 +3310,103 @@ YYLTYPE yylloc = yyloc_default; - else - (yyval.blk) = BLOCK(gen_subexp(gen_const(jv_object())), (yyvsp[-1].blk), gen_op_simple(POP)); - } --#line 3324 "src/parser.c" -+#line 3314 "src/parser.c" - break; - - case 97: /* Term: "reduce" Expr "as" Patterns '(' Query ';' Query ')' */ --#line 671 "src/parser.y" -+#line 661 "src/parser.y" - { - (yyval.blk) = gen_reduce((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk)); - } --#line 3332 "src/parser.c" -+#line 3322 "src/parser.c" - break; - - case 98: /* Term: "foreach" Expr "as" Patterns '(' Query ';' Query ';' Query ')' */ --#line 674 "src/parser.y" -+#line 664 "src/parser.y" - { - (yyval.blk) = gen_foreach((yyvsp[-9].blk), (yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk)); - } --#line 3340 "src/parser.c" -+#line 3330 "src/parser.c" - break; - - case 99: /* Term: "foreach" Expr "as" Patterns '(' Query ';' Query ')' */ --#line 677 "src/parser.y" -+#line 667 "src/parser.y" - { - (yyval.blk) = gen_foreach((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), gen_noop()); - } --#line 3348 "src/parser.c" -+#line 3338 "src/parser.c" - break; - - case 100: /* Term: "if" Query "then" Query ElseBody */ --#line 680 "src/parser.y" -+#line 670 "src/parser.y" - { - (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk)); - } --#line 3356 "src/parser.c" -+#line 3346 "src/parser.c" - break; - - case 101: /* Term: "if" Query "then" error */ --#line 683 "src/parser.y" -+#line 673 "src/parser.y" - { - FAIL((yyloc), "Possibly unterminated 'if' statement"); - (yyval.blk) = (yyvsp[-2].blk); - } --#line 3365 "src/parser.c" -+#line 3355 "src/parser.c" - break; - - case 102: /* Term: "try" Expr "catch" Expr */ --#line 687 "src/parser.y" -+#line 677 "src/parser.y" - { - (yyval.blk) = gen_try((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3373 "src/parser.c" -+#line 3363 "src/parser.c" - break; - - case 103: /* Term: "try" Expr "catch" error */ --#line 690 "src/parser.y" -+#line 680 "src/parser.y" - { - FAIL((yyloc), "Possibly unterminated 'try' statement"); - (yyval.blk) = (yyvsp[-2].blk); - } --#line 3382 "src/parser.c" -+#line 3372 "src/parser.c" - break; - - case 104: /* Term: "try" Expr */ --#line 694 "src/parser.y" -+#line 684 "src/parser.y" - { - (yyval.blk) = gen_try((yyvsp[0].blk), gen_op_simple(BACKTRACK)); - } --#line 3390 "src/parser.c" -+#line 3380 "src/parser.c" - break; - - case 105: /* Term: '$' '$' '$' BINDING */ --#line 712 "src/parser.y" -+#line 702 "src/parser.y" - { - (yyval.blk) = gen_location((yyloc), locations, gen_op_unbound(LOADVN, jv_string_value((yyvsp[0].literal)))); - jv_free((yyvsp[0].literal)); - } --#line 3399 "src/parser.c" -+#line 3389 "src/parser.c" - break; - - case 106: /* Term: BINDING */ --#line 716 "src/parser.y" -+#line 706 "src/parser.y" - { - (yyval.blk) = gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal)))); - jv_free((yyvsp[0].literal)); - } --#line 3408 "src/parser.c" -+#line 3398 "src/parser.c" - break; - - case 107: /* Term: "$__loc__" */ --#line 720 "src/parser.y" -+#line 710 "src/parser.y" - { - (yyval.blk) = gen_loc_object(&(yyloc), locations); - } --#line 3416 "src/parser.c" -+#line 3406 "src/parser.c" - break; - - case 108: /* Term: IDENT */ --#line 723 "src/parser.y" -+#line 713 "src/parser.y" - { - const char *s = jv_string_value((yyvsp[0].literal)); - if (strcmp(s, "false") == 0) -@@ -3429,198 +3419,198 @@ YYLTYPE yylloc = yyloc_default; - (yyval.blk) = gen_location((yyloc), locations, gen_call(s, gen_noop())); - jv_free((yyvsp[0].literal)); - } --#line 3433 "src/parser.c" -+#line 3423 "src/parser.c" - break; - - case 109: /* Term: IDENT '(' Args ')' */ --#line 735 "src/parser.y" -+#line 725 "src/parser.y" - { - (yyval.blk) = gen_call(jv_string_value((yyvsp[-3].literal)), (yyvsp[-1].blk)); - (yyval.blk) = gen_location((yylsp[-3]), locations, (yyval.blk)); - jv_free((yyvsp[-3].literal)); - } --#line 3443 "src/parser.c" -+#line 3433 "src/parser.c" - break; - - case 110: /* Term: '(' error ')' */ --#line 740 "src/parser.y" -+#line 730 "src/parser.y" - { (yyval.blk) = gen_noop(); } --#line 3449 "src/parser.c" -+#line 3439 "src/parser.c" - break; - - case 111: /* Term: '[' error ']' */ --#line 741 "src/parser.y" -+#line 731 "src/parser.y" - { (yyval.blk) = gen_noop(); } --#line 3455 "src/parser.c" -+#line 3445 "src/parser.c" - break; - - case 112: /* Term: Term '[' error ']' */ --#line 742 "src/parser.y" -+#line 732 "src/parser.y" - { (yyval.blk) = (yyvsp[-3].blk); } --#line 3461 "src/parser.c" -+#line 3451 "src/parser.c" - break; - - case 113: /* Term: '{' error '}' */ --#line 743 "src/parser.y" -+#line 733 "src/parser.y" - { (yyval.blk) = gen_noop(); } --#line 3467 "src/parser.c" -+#line 3457 "src/parser.c" - break; - - case 114: /* Args: Arg */ --#line 746 "src/parser.y" -+#line 736 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3475 "src/parser.c" -+#line 3465 "src/parser.c" - break; - - case 115: /* Args: Args ';' Arg */ --#line 749 "src/parser.y" -+#line 739 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3483 "src/parser.c" -+#line 3473 "src/parser.c" - break; - - case 116: /* Arg: Query */ --#line 754 "src/parser.y" -+#line 744 "src/parser.y" - { - (yyval.blk) = gen_lambda((yyvsp[0].blk)); - } --#line 3491 "src/parser.c" -+#line 3481 "src/parser.c" - break; - - case 117: /* RepPatterns: RepPatterns "?//" Pattern */ --#line 759 "src/parser.y" -+#line 749 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-2].blk), gen_destructure_alt((yyvsp[0].blk))); - } --#line 3499 "src/parser.c" -+#line 3489 "src/parser.c" - break; - - case 118: /* RepPatterns: Pattern */ --#line 762 "src/parser.y" -+#line 752 "src/parser.y" - { - (yyval.blk) = gen_destructure_alt((yyvsp[0].blk)); - } --#line 3507 "src/parser.c" -+#line 3497 "src/parser.c" - break; - - case 119: /* Patterns: RepPatterns "?//" Pattern */ --#line 767 "src/parser.y" -+#line 757 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3515 "src/parser.c" -+#line 3505 "src/parser.c" - break; - - case 120: /* Patterns: Pattern */ --#line 770 "src/parser.y" -+#line 760 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3523 "src/parser.c" -+#line 3513 "src/parser.c" - break; - - case 121: /* Pattern: BINDING */ --#line 775 "src/parser.y" -+#line 765 "src/parser.y" - { - (yyval.blk) = gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal))); - jv_free((yyvsp[0].literal)); - } --#line 3532 "src/parser.c" -+#line 3522 "src/parser.c" - break; - - case 122: /* Pattern: '[' ArrayPats ']' */ --#line 779 "src/parser.y" -+#line 769 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); - } --#line 3540 "src/parser.c" -+#line 3530 "src/parser.c" - break; - - case 123: /* Pattern: '{' ObjPats '}' */ --#line 782 "src/parser.y" -+#line 772 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP)); - } --#line 3548 "src/parser.c" -+#line 3538 "src/parser.c" - break; - - case 124: /* ArrayPats: Pattern */ --#line 787 "src/parser.y" -+#line 777 "src/parser.y" - { - (yyval.blk) = gen_array_matcher(gen_noop(), (yyvsp[0].blk)); - } --#line 3556 "src/parser.c" -+#line 3546 "src/parser.c" - break; - - case 125: /* ArrayPats: ArrayPats ',' Pattern */ --#line 790 "src/parser.y" -+#line 780 "src/parser.y" - { - (yyval.blk) = gen_array_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3564 "src/parser.c" -+#line 3554 "src/parser.c" - break; - - case 126: /* ObjPats: ObjPat */ --#line 795 "src/parser.y" -+#line 785 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3572 "src/parser.c" -+#line 3562 "src/parser.c" - break; - - case 127: /* ObjPats: ObjPats ',' ObjPat */ --#line 798 "src/parser.y" -+#line 788 "src/parser.y" - { - (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3580 "src/parser.c" -+#line 3570 "src/parser.c" - break; - - case 128: /* ObjPat: BINDING */ --#line 803 "src/parser.y" -+#line 793 "src/parser.y" - { - (yyval.blk) = gen_object_matcher(gen_const((yyvsp[0].literal)), gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal)))); - } --#line 3588 "src/parser.c" -+#line 3578 "src/parser.c" - break; - - case 129: /* ObjPat: BINDING ':' Pattern */ --#line 806 "src/parser.y" -+#line 796 "src/parser.y" - { - (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), BLOCK(gen_op_simple(DUP), gen_op_unbound(STOREV, jv_string_value((yyvsp[-2].literal))), (yyvsp[0].blk))); - } --#line 3596 "src/parser.c" -+#line 3586 "src/parser.c" - break; - - case 130: /* ObjPat: IDENT ':' Pattern */ --#line 809 "src/parser.y" -+#line 799 "src/parser.y" - { - (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); - } --#line 3604 "src/parser.c" -+#line 3594 "src/parser.c" - break; - - case 131: /* ObjPat: Keyword ':' Pattern */ --#line 812 "src/parser.y" -+#line 802 "src/parser.y" - { - (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); - } --#line 3612 "src/parser.c" -+#line 3602 "src/parser.c" - break; - - case 132: /* ObjPat: String ':' Pattern */ --#line 815 "src/parser.y" -+#line 805 "src/parser.y" - { - (yyval.blk) = gen_object_matcher((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3620 "src/parser.c" -+#line 3610 "src/parser.c" - break; - - case 133: /* ObjPat: '(' Query ')' ':' Pattern */ --#line 818 "src/parser.y" -+#line 808 "src/parser.y" - { - jv msg = check_object_key((yyvsp[-3].blk)); - if (jv_is_valid(msg)) { -@@ -3629,267 +3619,267 @@ YYLTYPE yylloc = yyloc_default; - jv_free(msg); - (yyval.blk) = gen_object_matcher((yyvsp[-3].blk), (yyvsp[0].blk)); - } --#line 3633 "src/parser.c" -+#line 3623 "src/parser.c" - break; - - case 134: /* ObjPat: error ':' Pattern */ --#line 826 "src/parser.y" -+#line 816 "src/parser.y" - { - FAIL((yyloc), "May need parentheses around object key expression"); - (yyval.blk) = (yyvsp[0].blk); - } --#line 3642 "src/parser.c" -+#line 3632 "src/parser.c" - break; - - case 135: /* Keyword: "as" */ --#line 832 "src/parser.y" -+#line 822 "src/parser.y" - { - (yyval.literal) = jv_string("as"); - } --#line 3650 "src/parser.c" -+#line 3640 "src/parser.c" - break; - - case 136: /* Keyword: "def" */ --#line 835 "src/parser.y" -+#line 825 "src/parser.y" - { - (yyval.literal) = jv_string("def"); - } --#line 3658 "src/parser.c" -+#line 3648 "src/parser.c" - break; - - case 137: /* Keyword: "module" */ --#line 838 "src/parser.y" -+#line 828 "src/parser.y" - { - (yyval.literal) = jv_string("module"); - } --#line 3666 "src/parser.c" -+#line 3656 "src/parser.c" - break; - - case 138: /* Keyword: "import" */ --#line 841 "src/parser.y" -+#line 831 "src/parser.y" - { - (yyval.literal) = jv_string("import"); - } --#line 3674 "src/parser.c" -+#line 3664 "src/parser.c" - break; - - case 139: /* Keyword: "include" */ --#line 844 "src/parser.y" -+#line 834 "src/parser.y" - { - (yyval.literal) = jv_string("include"); - } --#line 3682 "src/parser.c" -+#line 3672 "src/parser.c" - break; - - case 140: /* Keyword: "if" */ --#line 847 "src/parser.y" -+#line 837 "src/parser.y" - { - (yyval.literal) = jv_string("if"); - } --#line 3690 "src/parser.c" -+#line 3680 "src/parser.c" - break; - - case 141: /* Keyword: "then" */ --#line 850 "src/parser.y" -+#line 840 "src/parser.y" - { - (yyval.literal) = jv_string("then"); - } --#line 3698 "src/parser.c" -+#line 3688 "src/parser.c" - break; - - case 142: /* Keyword: "else" */ --#line 853 "src/parser.y" -+#line 843 "src/parser.y" - { - (yyval.literal) = jv_string("else"); - } --#line 3706 "src/parser.c" -+#line 3696 "src/parser.c" - break; - - case 143: /* Keyword: "elif" */ --#line 856 "src/parser.y" -+#line 846 "src/parser.y" - { - (yyval.literal) = jv_string("elif"); - } --#line 3714 "src/parser.c" -+#line 3704 "src/parser.c" - break; - - case 144: /* Keyword: "reduce" */ --#line 859 "src/parser.y" -+#line 849 "src/parser.y" - { - (yyval.literal) = jv_string("reduce"); - } --#line 3722 "src/parser.c" -+#line 3712 "src/parser.c" - break; - - case 145: /* Keyword: "foreach" */ --#line 862 "src/parser.y" -+#line 852 "src/parser.y" - { - (yyval.literal) = jv_string("foreach"); - } --#line 3730 "src/parser.c" -+#line 3720 "src/parser.c" - break; - - case 146: /* Keyword: "end" */ --#line 865 "src/parser.y" -+#line 855 "src/parser.y" - { - (yyval.literal) = jv_string("end"); - } --#line 3738 "src/parser.c" -+#line 3728 "src/parser.c" - break; - - case 147: /* Keyword: "and" */ --#line 868 "src/parser.y" -+#line 858 "src/parser.y" - { - (yyval.literal) = jv_string("and"); - } --#line 3746 "src/parser.c" -+#line 3736 "src/parser.c" - break; - - case 148: /* Keyword: "or" */ --#line 871 "src/parser.y" -+#line 861 "src/parser.y" - { - (yyval.literal) = jv_string("or"); - } --#line 3754 "src/parser.c" -+#line 3744 "src/parser.c" - break; - - case 149: /* Keyword: "try" */ --#line 874 "src/parser.y" -+#line 864 "src/parser.y" - { - (yyval.literal) = jv_string("try"); - } --#line 3762 "src/parser.c" -+#line 3752 "src/parser.c" - break; - - case 150: /* Keyword: "catch" */ --#line 877 "src/parser.y" -+#line 867 "src/parser.y" - { - (yyval.literal) = jv_string("catch"); - } --#line 3770 "src/parser.c" -+#line 3760 "src/parser.c" - break; - - case 151: /* Keyword: "label" */ --#line 880 "src/parser.y" -+#line 870 "src/parser.y" - { - (yyval.literal) = jv_string("label"); - } --#line 3778 "src/parser.c" -+#line 3768 "src/parser.c" - break; - - case 152: /* Keyword: "break" */ --#line 883 "src/parser.y" -+#line 873 "src/parser.y" - { - (yyval.literal) = jv_string("break"); - } --#line 3786 "src/parser.c" -+#line 3776 "src/parser.c" - break; - - case 153: /* DictPairs: %empty */ --#line 889 "src/parser.y" -+#line 879 "src/parser.y" - { - (yyval.blk) = gen_noop(); - } --#line 3794 "src/parser.c" -+#line 3784 "src/parser.c" - break; - - case 154: /* DictPairs: DictPair */ --#line 892 "src/parser.y" -+#line 882 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3802 "src/parser.c" -+#line 3792 "src/parser.c" - break; - - case 155: /* DictPairs: DictPair ',' DictPairs */ --#line 895 "src/parser.y" -+#line 885 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3810 "src/parser.c" -+#line 3800 "src/parser.c" - break; - - case 156: /* DictPair: IDENT ':' DictExpr */ --#line 900 "src/parser.y" -+#line 890 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); - } --#line 3818 "src/parser.c" -+#line 3808 "src/parser.c" - break; - - case 157: /* DictPair: Keyword ':' DictExpr */ --#line 903 "src/parser.y" -+#line 893 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk)); - } --#line 3826 "src/parser.c" -+#line 3816 "src/parser.c" - break; - - case 158: /* DictPair: String ':' DictExpr */ --#line 906 "src/parser.y" -+#line 896 "src/parser.y" - { - (yyval.blk) = gen_dictpair((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3834 "src/parser.c" -+#line 3824 "src/parser.c" - break; - - case 159: /* DictPair: String */ --#line 909 "src/parser.y" -+#line 899 "src/parser.y" - { - (yyval.blk) = gen_dictpair((yyvsp[0].blk), BLOCK(gen_op_simple(POP), gen_op_simple(DUP2), - gen_op_simple(DUP2), gen_op_simple(INDEX))); - } --#line 3843 "src/parser.c" -+#line 3833 "src/parser.c" - break; - - case 160: /* DictPair: BINDING ':' DictExpr */ --#line 913 "src/parser.y" -+#line 903 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[-2].literal)))), - (yyvsp[0].blk)); - jv_free((yyvsp[-2].literal)); - } --#line 3853 "src/parser.c" -+#line 3843 "src/parser.c" - break; - - case 161: /* DictPair: BINDING */ --#line 918 "src/parser.y" -+#line 908 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const((yyvsp[0].literal)), - gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal))))); - } --#line 3862 "src/parser.c" -+#line 3852 "src/parser.c" - break; - - case 162: /* DictPair: IDENT */ --#line 922 "src/parser.y" -+#line 912 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const(jv_copy((yyvsp[0].literal))), - gen_index(gen_noop(), gen_const((yyvsp[0].literal)))); - } --#line 3871 "src/parser.c" -+#line 3861 "src/parser.c" - break; - - case 163: /* DictPair: "$__loc__" */ --#line 926 "src/parser.y" -+#line 916 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const(jv_string("__loc__")), - gen_loc_object(&(yyloc), locations)); - } --#line 3880 "src/parser.c" -+#line 3870 "src/parser.c" - break; - - case 164: /* DictPair: Keyword */ --#line 930 "src/parser.y" -+#line 920 "src/parser.y" - { - (yyval.blk) = gen_dictpair(gen_const(jv_copy((yyvsp[0].literal))), - gen_index(gen_noop(), gen_const((yyvsp[0].literal)))); - } --#line 3889 "src/parser.c" -+#line 3879 "src/parser.c" - break; - - case 165: /* DictPair: '(' Query ')' ':' DictExpr */ --#line 934 "src/parser.y" -+#line 924 "src/parser.y" - { - jv msg = check_object_key((yyvsp[-3].blk)); - if (jv_is_valid(msg)) { -@@ -3898,36 +3888,36 @@ YYLTYPE yylloc = yyloc_default; - jv_free(msg); - (yyval.blk) = gen_dictpair((yyvsp[-3].blk), (yyvsp[0].blk)); - } --#line 3902 "src/parser.c" -+#line 3892 "src/parser.c" - break; - - case 166: /* DictPair: error ':' DictExpr */ --#line 942 "src/parser.y" -+#line 932 "src/parser.y" - { - FAIL((yylsp[-2]), "May need parentheses around object key expression"); - (yyval.blk) = (yyvsp[0].blk); - } --#line 3911 "src/parser.c" -+#line 3901 "src/parser.c" - break; - - case 167: /* DictExpr: DictExpr '|' DictExpr */ --#line 948 "src/parser.y" -+#line 938 "src/parser.y" - { - (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk)); - } --#line 3919 "src/parser.c" -+#line 3909 "src/parser.c" - break; - - case 168: /* DictExpr: Expr */ --#line 951 "src/parser.y" -+#line 941 "src/parser.y" - { - (yyval.blk) = (yyvsp[0].blk); - } --#line 3927 "src/parser.c" -+#line 3917 "src/parser.c" - break; - - --#line 3931 "src/parser.c" -+#line 3921 "src/parser.c" - - default: break; - } -@@ -4156,7 +4146,7 @@ YYLTYPE yylloc = yyloc_default; - return yyresult; - } - --#line 954 "src/parser.y" -+#line 944 "src/parser.y" - - - int jq_parse(struct locfile* locations, block* answer) { -diff --git a/src/parser.y b/src/parser.y -index 987a4ecaa3..ecd5796561 100644 ---- a/src/parser.y -+++ b/src/parser.y -@@ -439,26 +439,16 @@ ImportWhat Query ';' { - - 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/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch b/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch deleted file mode 100644 index 5f2f10d8af..0000000000 --- a/package/jq/0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 532ccea6080ed6758f39fe9f6208a44b665023d2 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Tue, 5 May 2026 22:44:02 +0900 -Subject: [PATCH] Limit recursive object merge depth to prevent stack overflow - -This fixes CVE-2026-43896. - -CVE: CVE-2026-43896 -Upstream: https://github.com/jqlang/jq/commit/532ccea6080ed6758f39fe9f6208a44b665023d2 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv.c | 25 +++++++++++++++++++++++-- - tests/jq.test | 9 +++++++++ - 2 files changed, 32 insertions(+), 2 deletions(-) - -diff --git a/src/jv.c b/src/jv.c -index feb68d1a1c..84fafef666 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -1933,16 +1933,33 @@ jv jv_object_merge(jv a, jv b) { - return a; - } - --jv jv_object_merge_recursive(jv a, jv b) { -+#ifndef MAX_OBJECT_MERGE_DEPTH -+#define MAX_OBJECT_MERGE_DEPTH (10000) -+#endif -+ -+static jv jvp_object_merge_recursive(jv a, jv b, int depth) { - assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); - assert(JVP_HAS_KIND(b, JV_KIND_OBJECT)); - -+ if (depth > MAX_OBJECT_MERGE_DEPTH) { -+ jv_free(a); -+ jv_free(b); -+ return jv_invalid_with_msg(jv_string("Object merge too deep")); -+ } -+ - jv_object_foreach(b, k, v) { - jv elem = jv_object_get(jv_copy(a), jv_copy(k)); - if (jv_is_valid(elem) && - JVP_HAS_KIND(elem, JV_KIND_OBJECT) && - JVP_HAS_KIND(v, JV_KIND_OBJECT)) { -- a = jv_object_set(a, k, jv_object_merge_recursive(elem, v)); -+ jv merged = jvp_object_merge_recursive(elem, v, depth + 1); -+ if (!jv_is_valid(merged)) { -+ jv_free(k); -+ jv_free(a); -+ jv_free(b); -+ return merged; -+ } -+ a = jv_object_set(a, k, merged); - } else { - jv_free(elem); - a = jv_object_set(a, k, v); -@@ -1953,6 +1970,10 @@ jv jv_object_merge_recursive(jv a, jv b) { - return a; - } - -+jv jv_object_merge_recursive(jv a, jv b) { -+ return jvp_object_merge_recursive(a, b, 0); -+} -+ - /* - * Object iteration (internal helpers) - */ diff --git a/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch b/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch deleted file mode 100644 index ca82a3a744..0000000000 --- a/package/jq/0012-detect-circular-module-imports-to-prevent-stack-overflow.patch +++ /dev/null @@ -1,142 +0,0 @@ -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: https://github.com/jqlang/jq/commit/f58787c41835d9b17795730cb04925fdba25c71c -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/linker.c | 59 ++++++++++++++++++++++++------------- - 6 files changed, 70 insertions(+), 20 deletions(-) - -diff --git a/src/linker.c b/src/linker.c -index e9027004cc..03f46db05c 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, -@@ -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,30 +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; - } - } -- 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); -@@ -413,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; -@@ -439,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/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch b/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch deleted file mode 100644 index bfcb6a12b2..0000000000 --- a/package/jq/0013-guard-deep-structural-equality-and-comparison-recursion.patch +++ /dev/null @@ -1,423 +0,0 @@ -From 7122866869960b55cea3646bc91334ef55787831 Mon Sep 17 00:00:00 2001 -From: Yu-Fu Fu <yufu@yfu.tw> -Date: Fri, 22 May 2026 04:07:16 -0700 -Subject: [PATCH] Guard deep structural equality and comparison recursion - (#3539) - -jv_equal and jv_cmp overflows the C stack on deeply nested -input. Cap recursion at 10000 with -1 / INT_MIN sentinels; -operators that compose user expressions surface this as -"Equality check too deep" / "Comparison too deep". - -Fixes CVE-2026-47770. - -CVE: CVE-2026-47770 -Upstream: https://github.com/jqlang/jq/commit/7122866869960b55cea3646bc91334ef55787831 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/builtin.c | 36 +++++++++++++++-- - src/jv.c | 46 +++++++++++++++++----- - src/jv_aux.c | 104 ++++++++++++++++++++++++++++++++++++++++++-------- - 4 files changed, 180 insertions(+), 28 deletions(-) - -diff --git a/src/builtin.c b/src/builtin.c -index 2b2a2d40da..8ee605ed2b 100644 ---- a/src/builtin.c -+++ b/src/builtin.c -@@ -295,7 +295,15 @@ jv binop_minus(jv a, jv b) { - jv_array_foreach(a, i, x) { - int include = 1; - jv_array_foreach(b, j, y) { -- if (jv_equal(jv_copy(x), y)) { -+ int equal = jv_equal(jv_copy(x), y); -+ if (equal < 0) { -+ jv_free(out); -+ jv_free(x); -+ jv_free(a); -+ jv_free(b); -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ } -+ if (equal) { - include = 0; - break; - } -@@ -379,11 +387,17 @@ jv binop_mod(jv a, jv b) { - #undef dtoi - - jv binop_equal(jv a, jv b) { -- return jv_bool(jv_equal(a, b)); -+ int r = jv_equal(a, b); -+ if (r < 0) -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ return jv_bool(r); - } - - jv binop_notequal(jv a, jv b) { -- return jv_bool(!jv_equal(a, b)); -+ int r = jv_equal(a, b); -+ if (r < 0) -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ return jv_bool(!r); - } - - enum cmp_op { -@@ -395,6 +409,8 @@ enum cmp_op { - - static jv order_cmp(jv a, jv b, enum cmp_op op) { - int r = jv_cmp(a, b); -+ if (r == INT_MIN) -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); - return jv_bool((op == CMP_OP_LESS && r < 0) || - (op == CMP_OP_LESSEQ && r <= 0) || - (op == CMP_OP_GREATEREQ && r >= 0) || -@@ -848,6 +864,12 @@ static jv f_bsearch(jq_state *jq, jv input, jv target) { - while (start < end) { - int mid = start + (end - start) / 2; - int result = jv_cmp(jv_copy(target), jv_array_get(jv_copy(input), mid)); -+ if (result == INT_MIN) { -+ jv_free(answer); -+ jv_free(input); -+ jv_free(target); -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); -+ } - if (result == 0) { - answer = jv_number(mid); - break; -@@ -1139,6 +1161,14 @@ static jv minmax_by(jv values, jv keys, int is_min) { - for (int i=1; i<jv_array_length(jv_copy(values)); i++) { - jv item = jv_array_get(jv_copy(keys), i); - int cmp = jv_cmp(jv_copy(item), jv_copy(retkey)); -+ if (cmp == INT_MIN) { -+ jv_free(item); -+ jv_free(values); -+ jv_free(keys); -+ jv_free(retkey); -+ jv_free(ret); -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); -+ } - if ((cmp < 0) == (is_min == 1)) { - jv_free(retkey); - retkey = item; -diff --git a/src/jv.c b/src/jv.c -index 074ee310c5..bcd7c9b5b0 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -915,16 +915,20 @@ static jv* jvp_array_write(jv* a, int i) { - } - } - --static int jvp_array_equal(jv a, jv b) { -+static int jvp_equal(jv a, jv b, int depth); -+ -+static int jvp_array_equal(jv a, jv b, int depth) { - if (jvp_array_length(a) != jvp_array_length(b)) - return 0; - if (jvp_array_ptr(a) == jvp_array_ptr(b) && - jvp_array_offset(a) == jvp_array_offset(b)) - return 1; - for (int i=0; i<jvp_array_length(a); i++) { -- if (!jv_equal(jv_copy(*jvp_array_read(a, i)), -- jv_copy(*jvp_array_read(b, i)))) -- return 0; -+ int r = jvp_equal(jv_copy(*jvp_array_read(a, i)), -+ jv_copy(*jvp_array_read(b, i)), -+ depth); -+ if (r <= 0) -+ return r; - } - return 1; - } -@@ -1074,7 +1078,14 @@ jv jv_array_indexes(jv a, jv b) { - int alen = jv_array_length(jv_copy(a)); - for (int ai = 0; ai < alen; ++ai) { - jv_array_foreach(b, bi, belem) { -- if (!jv_equal(jv_array_get(jv_copy(a), ai + bi), belem)) -+ int equal = jv_equal(jv_array_get(jv_copy(a), ai + bi), belem); -+ if (equal < 0) { -+ jv_free(res); -+ jv_free(a); -+ jv_free(b); -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ } -+ if (!equal) - idx = -1; - else if (bi == 0 && idx == -1) - idx = ai; -@@ -1831,7 +1842,7 @@ static int jvp_object_length(jv object) { - return n; - } - --static int jvp_object_equal(jv o1, jv o2) { -+static int jvp_object_equal(jv o1, jv o2, int depth) { - int len2 = jvp_object_length(o2); - int len1 = 0; - for (int i=0; i<jvp_object_size(o1); i++) { -@@ -1840,7 +1851,8 @@ static int jvp_object_equal(jv o1, jv o2) { - jv* slot2 = jvp_object_read(o2, slot->string); - if (!slot2) return 0; - // FIXME: do less refcounting here -- if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0; -+ int r = jvp_equal(jv_copy(slot->value), jv_copy(*slot2), depth); -+ if (r <= 0) return r; - len1++; - } - return len1 == len2; -@@ -2056,7 +2068,16 @@ int jv_get_refcnt(jv j) { - * Higher-level operations - */ - --int jv_equal(jv a, jv b) { -+#ifndef MAX_EQUAL_DEPTH -+#define MAX_EQUAL_DEPTH (10000) -+#endif -+ -+static int jvp_equal(jv a, jv b, int depth) { -+ if (depth > MAX_EQUAL_DEPTH) { -+ jv_free(a); -+ jv_free(b); -+ return -1; -+ } - int r; - if (jv_get_kind(a) != jv_get_kind(b)) { - r = 0; -@@ -2072,13 +2093,13 @@ int jv_equal(jv a, jv b) { - r = jvp_number_equal(a, b); - break; - case JV_KIND_ARRAY: -- r = jvp_array_equal(a, b); -+ r = jvp_array_equal(a, b, depth + 1); - break; - case JV_KIND_STRING: - r = jvp_string_equal(a, b); - break; - case JV_KIND_OBJECT: -- r = jvp_object_equal(a, b); -+ r = jvp_object_equal(a, b, depth + 1); - break; - default: - r = 1; -@@ -2090,6 +2111,11 @@ int jv_equal(jv a, jv b) { - return r; - } - -+// Returns 1 if equal, 0 if not equal, or -1 if the comparison is too deep -+int jv_equal(jv a, jv b) { -+ return jvp_equal(a, b, 0); -+} -+ - int jv_identical(jv a, jv b) { - int r; - if (a.kind_flags != b.kind_flags -diff --git a/src/jv_aux.c b/src/jv_aux.c -index fd5ff96684..4f5be0ab14 100644 ---- a/src/jv_aux.c -+++ b/src/jv_aux.c -@@ -16,6 +16,24 @@ static double jv_number_get_value_and_consume(jv number) { - return value; - } - -+#ifndef MAX_CMP_DEPTH -+#define MAX_CMP_DEPTH (10000) -+#endif -+ -+struct sort_cmp_state { -+ int too_deep; -+}; -+ -+#ifdef _MSC_VER -+static __declspec(thread) struct sort_cmp_state sort_cmp_state; -+#else -+#ifdef HAVE___THREAD -+static __thread struct sort_cmp_state sort_cmp_state; -+#else -+static struct sort_cmp_state sort_cmp_state; -+#endif -+#endif -+ - static jv parse_slice(jv j, jv slice, int* pstart, int* pend) { - // Array slices - jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); -@@ -606,7 +624,13 @@ jv jv_keys(jv x) { - } - } - --int jv_cmp(jv a, jv b) { -+static int jvp_cmp(jv a, jv b, int depth) { -+ if (depth > MAX_CMP_DEPTH) { -+ jv_free(a); -+ jv_free(b); -+ return INT_MIN; -+ } -+ - if (jv_get_kind(a) != jv_get_kind(b)) { - int r = (int)jv_get_kind(a) - (int)jv_get_kind(b); - jv_free(a); -@@ -621,14 +645,13 @@ int jv_cmp(jv a, jv b) { - case JV_KIND_FALSE: - case JV_KIND_TRUE: - // there's only one of each of these values -- r = 0; - break; - - case JV_KIND_NUMBER: { - if (jvp_number_is_nan(a)) { -- r = jv_cmp(jv_null(), jv_copy(b)); -+ r = jvp_cmp(jv_null(), jv_copy(b), depth); - } else if (jvp_number_is_nan(b)) { -- r = jv_cmp(jv_copy(a), jv_null()); -+ r = jvp_cmp(jv_copy(a), jv_null(), depth); - } else { - r = jvp_number_cmp(a, b); - } -@@ -652,7 +675,9 @@ int jv_cmp(jv a, jv b) { - } - jv xa = jv_array_get(jv_copy(a), i); - jv xb = jv_array_get(jv_copy(b), i); -- r = jv_cmp(xa, xb); -+ r = jvp_cmp(xa, xb, depth + 1); -+ if (r == INT_MIN) -+ break; - i++; - } - break; -@@ -661,13 +686,14 @@ int jv_cmp(jv a, jv b) { - case JV_KIND_OBJECT: { - jv keys_a = jv_keys(jv_copy(a)); - jv keys_b = jv_keys(jv_copy(b)); -- r = jv_cmp(jv_copy(keys_a), keys_b); -+ r = jvp_cmp(jv_copy(keys_a), keys_b, depth + 1); - if (r == 0) { - jv_array_foreach(keys_a, i, key) { - jv xa = jv_object_get(jv_copy(a), jv_copy(key)); - jv xb = jv_object_get(jv_copy(b), key); -- r = jv_cmp(xa, xb); -- if (r) break; -+ r = jvp_cmp(xa, xb, depth + 1); -+ if (r != 0) -+ break; - } - } - jv_free(keys_a); -@@ -680,6 +706,11 @@ int jv_cmp(jv a, jv b) { - return r; - } - -+// Returns <0, 0, >0 if a is less than, equal to, or greater than b, or -+// INT_MIN if the comparison is too deep -+int jv_cmp(jv a, jv b) { -+ return jvp_cmp(a, b, 0); -+} - - struct sort_entry { - jv object; -@@ -687,19 +718,32 @@ struct sort_entry { - int index; - }; - -+static void sort_entry_array_free(struct sort_entry* entries, int start, int n) { -+ for (int i = start; i < n; i++) { -+ jv_free(entries[i].key); -+ jv_free(entries[i].object); -+ } -+ jv_mem_free(entries); -+} -+ - static int sort_cmp(const void* pa, const void* pb) { - const struct sort_entry* a = pa; - const struct sort_entry* b = pb; - int r = jv_cmp(jv_copy(a->key), jv_copy(b->key)); -+ if (r == INT_MIN) { -+ sort_cmp_state.too_deep = 1; -+ return 0; -+ } - // comparing by index if r == 0 makes the sort stable - return r ? r : (a->index - b->index); - } - --static struct sort_entry* sort_items(jv objects, jv keys) { -+static struct sort_entry* sort_items(jv objects, jv keys, int *too_deep) { - assert(jv_get_kind(objects) == JV_KIND_ARRAY); - assert(jv_get_kind(keys) == JV_KIND_ARRAY); - assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); - int n = jv_array_length(jv_copy(objects)); -+ *too_deep = 0; - if (n == 0) { - jv_free(objects); - jv_free(keys); -@@ -713,7 +757,13 @@ static struct sort_entry* sort_items(jv objects, jv keys) { - } - jv_free(objects); - jv_free(keys); -+ sort_cmp_state.too_deep = 0; - qsort(entries, n, sizeof(struct sort_entry), sort_cmp); -+ if (sort_cmp_state.too_deep) { -+ sort_entry_array_free(entries, 0, n); -+ *too_deep = 1; -+ return NULL; -+ } - return entries; - } - -@@ -722,7 +772,10 @@ jv jv_sort(jv objects, jv keys) { - assert(jv_get_kind(keys) == JV_KIND_ARRAY); - assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); - int n = jv_array_length(jv_copy(objects)); -- struct sort_entry* entries = sort_items(objects, keys); -+ int too_deep = 0; -+ struct sort_entry* entries = sort_items(objects, keys, &too_deep); -+ if (too_deep) -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); - jv ret = jv_array(); - for (int i=0; i<n; i++) { - jv_free(entries[i].key); -@@ -737,13 +790,24 @@ jv jv_group(jv objects, jv keys) { - assert(jv_get_kind(keys) == JV_KIND_ARRAY); - assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); - int n = jv_array_length(jv_copy(objects)); -- struct sort_entry* entries = sort_items(objects, keys); -+ int too_deep = 0; -+ struct sort_entry* entries = sort_items(objects, keys, &too_deep); -+ if (too_deep) -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); - jv ret = jv_array(); - if (n > 0) { - jv curr_key = entries[0].key; - jv group = jv_array_append(jv_array(), entries[0].object); - for (int i = 1; i < n; i++) { -- if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) { -+ int equal = jv_equal(jv_copy(curr_key), jv_copy(entries[i].key)); -+ if (equal < 0) { -+ jv_free(curr_key); -+ jv_free(group); -+ sort_entry_array_free(entries, i, n); -+ jv_free(ret); -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ } -+ if (equal) { - jv_free(entries[i].key); - } else { - jv_free(curr_key); -@@ -765,11 +829,21 @@ jv jv_unique(jv objects, jv keys) { - assert(jv_get_kind(keys) == JV_KIND_ARRAY); - assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); - int n = jv_array_length(jv_copy(objects)); -- struct sort_entry* entries = sort_items(objects, keys); -+ int too_deep = 0; -+ struct sort_entry* entries = sort_items(objects, keys, &too_deep); -+ if (too_deep) -+ return jv_invalid_with_msg(jv_string("Comparison too deep")); - jv ret = jv_array(); - jv curr_key = jv_invalid(); - for (int i = 0; i < n; i++) { -- if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) { -+ int equal = jv_equal(jv_copy(curr_key), jv_copy(entries[i].key)); -+ if (equal < 0) { -+ jv_free(curr_key); -+ sort_entry_array_free(entries, i, n); -+ jv_free(ret); -+ return jv_invalid_with_msg(jv_string("Equality check too deep")); -+ } -+ if (equal) { - jv_free(entries[i].key); - jv_free(entries[i].object); - } else { diff --git a/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch b/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch deleted file mode 100644 index c1b0dadfdb..0000000000 --- a/package/jq/0014-fix-heap-buffer-overflow-in-raw-file-loading.patch +++ /dev/null @@ -1,32 +0,0 @@ -From e987df0d463d85fd70825e042a082427e8275b86 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Mon, 8 Jun 2026 22:14:48 +0900 -Subject: [PATCH] Fix heap-buffer-overflow in raw file loading - -When `jv_string_append_buf` overflows the string length limit, -it returns an invalid `jv`; `jv_load_file` then re-entered it -on the invalid value and overran the heap. Break out of the loop -once the value is invalid. - -Fixes CVE-2026-49839. - -CVE: CVE-2026-49839 -Upstream: https://github.com/jqlang/jq/commit/e987df0d463d85fd70825e042a082427e8275b86 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/jv_file.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/jv_file.c b/src/jv_file.c -index 7706b0e06e..fbc1e4d653 100644 ---- a/src/jv_file.c -+++ b/src/jv_file.c -@@ -57,6 +57,8 @@ jv jv_load_file(const char* filename, int raw) { - - if (raw) { - data = jv_string_append_buf(data, buf, n); -+ if (!jv_is_valid(data)) -+ break; - } else { - jv_parser_set_buf(parser, buf, n, !feof(file)); - jv value; diff --git a/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch b/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch deleted file mode 100644 index 606c818a41..0000000000 --- a/package/jq/0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 46d1da30944ce93dd671ac72b6513fc0eb747837 Mon Sep 17 00:00:00 2001 -From: itchyny <itchyny@cybozu.co.jp> -Date: Tue, 16 Jun 2026 14:31:14 +0900 -Subject: [PATCH] Tighten string length bounds and propagate invalid jv in - implode -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The bound added in CVE-2026-32316 (e47e56d22) still allowed -`sizeof(jvp_string) + (currlen + len) * 2 + 1` to wrap `size_t` on -32-bit platforms. Tighten the threshold so the final allocation -fits in 32-bit `size_t`. - -Also break out of `jv_string_implode` and `f_string_implode` once -`jv_string_append_codepoint` returns an invalid `jv`; otherwise the -next iteration triggers the assertion in `jvp_string_ptr` (or -invokes undefined behavior under `-DNDEBUG`). - -Fixes CVE-2026-54679. - -Co-authored-by: Dirk Müller <dirk@dmllr.de> - -CVE: CVE-2026-54679 -Upstream: https://github.com/jqlang/jq/commit/46d1da30944ce93dd671ac72b6513fc0eb747837 -Signed-off-by: Thomas Perale <thomas.perale@mind.be> ---- - src/builtin.c | 1 + - src/jv.c | 5 +++-- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/src/builtin.c b/src/builtin.c -index ed205d9935..a3b7a61ae8 100644 ---- a/src/builtin.c -+++ b/src/builtin.c -@@ -1396,6 +1396,7 @@ static jv f_string_implode(jq_state *jq, jv a) { - if (nv < 0 || nv > 0x10FFFF || (nv >= 0xD800 && nv <= 0xDFFF)) - nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER - s = jv_string_append_codepoint(s, nv); -+ if (!jv_is_valid(s)) break; - } - - jv_free(a); -diff --git a/src/jv.c b/src/jv.c -index bcd7c9b5b0..48a63e6e55 100644 ---- a/src/jv.c -+++ b/src/jv.c -@@ -1197,7 +1197,7 @@ static uint32_t jvp_string_remaining_space(jvp_string* s) { - static jv jvp_string_append(jv string, const char* data, uint32_t len) { - jvp_string* s = jvp_string_ptr(string); - uint32_t currlen = jvp_string_length(s); -- if ((uint64_t)currlen + len >= INT_MAX) { -+ if ((uint64_t)currlen + len >= INT_MAX - sizeof(jvp_string) / 2) { - jv_free(string); - return jv_invalid_with_msg(jv_string("String too long")); - } -@@ -1398,7 +1398,7 @@ jv jv_string_repeat(jv j, int n) { - } - int len = jv_string_length_bytes(jv_copy(j)); - int64_t res_len = (int64_t)len * n; -- if (res_len >= INT_MAX) { -+ if ((uint64_t)res_len >= INT_MAX - sizeof(jvp_string) / 2) { - jv_free(j); - return jv_invalid_with_msg(jv_string("Repeat string result too long")); - } -@@ -1483,6 +1483,7 @@ jv jv_string_implode(jv j) { - if (nv < 0 || nv > 0x10FFFF || (nv >= 0xD800 && nv <= 0xDFFF)) - nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER - s = jv_string_append_codepoint(s, nv); -+ if (!jv_is_valid(s)) break; - } - - jv_free(j); diff --git a/package/jq/jq.hash b/package/jq/jq.hash index 4596134620..d965a2af78 100644 --- a/package/jq/jq.hash +++ b/package/jq/jq.hash @@ -1,3 +1,3 @@ # Locally calculated -sha256 2be64e7129cecb11d5906290eba10af694fb9e3e7f9fc208a311dc33ca837eb0 jq-1.8.1.tar.gz +sha256 71b8d6e8f5fe81f6c6d0d110e3892251f6ce76ed095abd315e26e6e1193af3af jq-1.8.2.tar.gz sha256 ad2b4a266b2268939c1446979759706077421cf906a203aa188c6f396e8cfd74 COPYING diff --git a/package/jq/jq.mk b/package/jq/jq.mk index eaa04846e0..83dfe4381a 100644 --- a/package/jq/jq.mk +++ b/package/jq/jq.mk @@ -4,7 +4,7 @@ # ################################################################################ -JQ_VERSION = 1.8.1 +JQ_VERSION = 1.8.2 JQ_SITE = https://github.com/jqlang/jq/releases/download/jq-$(JQ_VERSION) JQ_LICENSE = MIT (code), ICU (decNumber), CC-BY-3.0 (documentation), \ BSD-2-Clause (strptime) @@ -12,51 +12,6 @@ JQ_LICENSE_FILES = COPYING JQ_CPE_ID_VENDOR = jqlang JQ_INSTALL_STAGING = YES -# 0001-fix-out-of-bounds-read-in-jv-parse-sized.patch -JQ_IGNORE_CVES += CVE-2026-39979 - -# 0002-fix-NUL-truncation-in-the-JSON-parser.patch -JQ_IGNORE_CVES += CVE-2026-33948 - -# 0003-limit-path-depth-to-prevent-stack-overflow.patch -JQ_IGNORE_CVES += CVE-2026-33947 - -# 0004-fix-heap-buffer-overflow-in-jvp-string-append-and-jvp-string-copy-replace-bad.patch -JQ_IGNORE_CVES += CVE-2026-32316 - -# 0005-randomize-hash-seed-to-mitigate-hash-collision-DoS-attacks.patch -JQ_IGNORE_CVES += CVE-2026-40164 - -# 0006-limit-the-containment-check-depth.patch -JQ_IGNORE_CVES += 8:CVE: CVE-2026-40612 - -# 0007-fix-NUL-truncation-in-program-files-loaded-with-f.patch -JQ_IGNORE_CVES += CVE-2026-41256 - -# 0008-fix-signed-int-overflow-in-stack-reallocate.patch -JQ_IGNORE_CVES += CVE-2026-41257 - -# 0009-reject-numeric-literals-longer-than-DEC-MAX-DIGITS.patch -JQ_IGNORE_CVES += CVE-2026-43894 - -# 0010-reject-embedded-NUL-bytes-in-module-import-paths.patch -JQ_IGNORE_CVES += CVE-2026-43895 - -# 0011-limit-recursive-object-merge-depth-to-prevent-stack-overflow.patch -JQ_IGNORE_CVES += CVE-2026-43896 - -# 0012-detect-circular-module-imports-to-prevent-stack-overflow.patch -JQ_IGNORE_CVES += CVE-2026-44777 - -# 0013-guard-deep-structural-equality-and-comparison-recursion.patch -JQ_IGNORE_CVES += CVE-2026-47770 - -# 0014-fix-heap-buffer-overflow-in-raw-file-loading.patch -JQ_IGNORE_CVES += CVE-2026-49839 - -# 0015-tighten-string-length-bounds-and-propagate-invalid-jv-in-implode.patch -JQ_IGNORE_CVES += CVE-2026-54679 - # uses c99 specific features JQ_CONF_ENV += CFLAGS="$(TARGET_CFLAGS) -std=c99" HOST_JQ_CONF_ENV += CFLAGS="$(HOST_CFLAGS) -std=c99" -- 2.54.0 _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 2026-06-30 20:15 ` [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 Thomas Perale via buildroot @ 2026-07-02 6:44 ` Peter Korsgaard 2026-07-02 8:46 ` Thomas Perale via buildroot 0 siblings, 1 reply; 4+ messages in thread From: Peter Korsgaard @ 2026-07-02 6:44 UTC (permalink / raw) To: Thomas Perale via buildroot Cc: Thomas Perale, Angelo Compagnucci, Danomi Manchego >>>>> "Thomas" == Thomas Perale via buildroot <buildroot@buildroot.org> writes: > For more information about the release, see: > - https://github.com/jqlang/jq/releases/tag/jq-1.8.2 > All the CVEs were addressed by a patch but non-CVEs security fixes such > as GHSA-gf4g-95wj-4q4r or GHSA-hj52-j2c9-r8r4 are fixed by this release. > Signed-off-by: Thomas Perale <thomas.perale@mind.be> I don't really think it makes a lot of sense to do this in two steps as you presumably want to backport the 1.8.2 bugfix bump to the stable series as well? But anyway, committed series - Thanks. -- Bye, Peter Korsgaard _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 2026-07-02 6:44 ` Peter Korsgaard @ 2026-07-02 8:46 ` Thomas Perale via buildroot 0 siblings, 0 replies; 4+ messages in thread From: Thomas Perale via buildroot @ 2026-07-02 8:46 UTC (permalink / raw) To: Peter Korsgaard, Thomas Perale via buildroot Cc: Angelo Compagnucci, Danomi Manchego Hi Peter, On 7/2/26 8:44 AM, Peter Korsgaard wrote: >>>>>> "Thomas" == Thomas Perale via buildroot <buildroot@buildroot.org> writes: > > For more information about the release, see: > > - https://github.com/jqlang/jq/releases/tag/jq-1.8.2 > > > All the CVEs were addressed by a patch but non-CVEs security fixes such > > as GHSA-gf4g-95wj-4q4r or GHSA-hj52-j2c9-r8r4 are fixed by this release. > > > Signed-off-by: Thomas Perale <thomas.perale@mind.be> > > I don't really think it makes a lot of sense to do this in two steps as > you presumably want to backport the 1.8.2 bugfix bump to the stable > series as well? > > But anyway, committed series - Thanks. Yes it's unnecessary but it's to avoid having to send a different backport series for the LTS branch based on 1.7.1. Thanks ! PERALE Thomas _______________________________________________ buildroot mailing list buildroot@buildroot.org https://lists.buildroot.org/mailman/listinfo/buildroot ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-02 8:46 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-30 20:15 [Buildroot] [PATCH 1/2] package/jq: patch various CVEs Thomas Perale via buildroot 2026-06-30 20:15 ` [Buildroot] [PATCH 2/2] package/jq: security bump to v1.8.2 Thomas Perale via buildroot 2026-07-02 6:44 ` Peter Korsgaard 2026-07-02 8:46 ` Thomas Perale via buildroot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox