From 9daee866d1b4567a40c7af8067f0ade213a7091d Mon Sep 17 00:00:00 2001 From: Colin McAllister Date: Mon, 7 Jul 2025 16:39:43 -0500 Subject: jq: Fix CVEs Adds backported patches to fix CVE-2024-23339, CVE-2024-53427, and CVE-2025-48060. Signed-off-by: Colin Pinnell McAllister Change-Id: Ibc2db956b7fd5d0388dbed1a81ddf9aa58431fb1 Signed-off-by: Armin Kuster --- .../recipes-devtools/jq/jq/CVE-2024-23337.patch | 219 +++++++++++++++++++++ .../recipes-devtools/jq/jq/CVE-2024-53427-01.patch | 69 +++++++ .../recipes-devtools/jq/jq/CVE-2024-53427-02.patch | 56 ++++++ .../recipes-devtools/jq/jq/CVE-2025-48060.patch | 46 +++++ meta-oe/recipes-devtools/jq/jq_git.bb | 8 +- 5 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-01.patch create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-02.patch create mode 100644 meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch new file mode 100644 index 0000000000..87e639aad7 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch @@ -0,0 +1,219 @@ +From 35cde320ac7ee9ad6da5ce422922fafe592c4c60 Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Wed, 21 May 2025 07:45:00 +0900 +Subject: [PATCH 1/2] Fix signed integer overflow in jvp_array_write and + jvp_object_rehash + +This commit fixes signed integer overflow and SEGV issues on growing +arrays and objects. The size of arrays and objects is now limited to +`536870912` (`0x20000000`). This fixes CVE-2024-23337 and fixes #3262. + +CVE: CVE-2024-23337 +Upstream-Status: Backport [https://github.com/jqlang/jq/commit/de21386681c0df0104a99d9d09db23a9b2a78b1e] +Signed-off-by: Colin Pinnell McAllister +--- + src/jv.c | 45 ++++++++++++++++++++++++++++++++++++--------- + src/jv_aux.c | 9 +++++---- + tests/jq.test | 4 ++++ + 3 files changed, 45 insertions(+), 13 deletions(-) + +diff --git a/src/jv.c b/src/jv.c +index 9784b22..33ccee9 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1006,6 +1006,11 @@ jv jv_array_set(jv j, int idx, jv val) { + jv_free(val); + return jv_invalid_with_msg(jv_string("Out of bounds negative array index")); + } ++ if (idx > (INT_MAX >> 2) - jvp_array_offset(j)) { ++ jv_free(j); ++ jv_free(val); ++ return jv_invalid_with_msg(jv_string("Array index too large")); ++ } + // copy/free of val,j coalesced + jv* slot = jvp_array_write(&j, idx); + jv_free(*slot); +@@ -1025,6 +1030,7 @@ jv jv_array_concat(jv a, jv b) { + // FIXME: could be faster + jv_array_foreach(b, i, elem) { + a = jv_array_append(a, elem); ++ if (!jv_is_valid(a)) break; + } + jv_free(b); + return a; +@@ -1296,6 +1302,7 @@ jv jv_string_indexes(jv j, jv k) { + p = jstr; + while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) { + a = jv_array_append(a, jv_number(p - jstr)); ++ if (!jv_is_valid(a)) break; + p += idxlen; + } + } +@@ -1318,14 +1325,17 @@ jv jv_string_split(jv j, jv sep) { + + if (seplen == 0) { + int c; +- while ((jstr = jvp_utf8_next(jstr, jend, &c))) ++ while ((jstr = jvp_utf8_next(jstr, jend, &c))) { + a = jv_array_append(a, jv_string_append_codepoint(jv_string(""), c)); ++ if (!jv_is_valid(a)) break; ++ } + } else { + for (p = jstr; p < jend; p = s + seplen) { + s = _jq_memmem(p, jend - p, sepstr, seplen); + if (s == NULL) + s = jend; + a = jv_array_append(a, jv_string_sized(p, s - p)); ++ if (!jv_is_valid(a)) break; + // Add an empty string to denote that j ends on a sep + if (s + seplen == jend && seplen != 0) + a = jv_array_append(a, jv_string("")); +@@ -1343,8 +1353,10 @@ jv jv_string_explode(jv j) { + const char* end = i + len; + jv a = jv_array_sized(len); + int c; +- while ((i = jvp_utf8_next(i, end, &c))) ++ while ((i = jvp_utf8_next(i, end, &c))) { + a = jv_array_append(a, jv_number(c)); ++ if (!jv_is_valid(a)) break; ++ } + jv_free(j); + return a; + } +@@ -1617,10 +1629,13 @@ static void jvp_object_free(jv o) { + } + } + +-static jv jvp_object_rehash(jv object) { ++static int jvp_object_rehash(jv *objectp) { ++ jv object = *objectp; + assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); + assert(jvp_refcnt_unshared(object.u.ptr)); + int size = jvp_object_size(object); ++ if (size > INT_MAX >> 2) ++ return 0; + jv new_object = jvp_object_new(size * 2); + for (int i=0; ivalue; ++ *valpp = &slot->value; ++ return 1; + } + slot = jvp_object_add_slot(*object, key, bucket); + if (slot) { + slot->value = jv_invalid(); + } else { +- *object = jvp_object_rehash(*object); ++ if (!jvp_object_rehash(object)) { ++ *valpp = NULL; ++ return 0; ++ } + bucket = jvp_object_find_bucket(*object, key); + assert(!jvp_object_find_slot(*object, key, bucket)); + slot = jvp_object_add_slot(*object, key, bucket); + assert(slot); + slot->value = jv_invalid(); + } +- return &slot->value; ++ *valpp = &slot->value; ++ return 1; + } + + static int jvp_object_delete(jv* object, jv key) { +@@ -1783,7 +1804,11 @@ jv jv_object_set(jv object, jv key, jv value) { + assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); + assert(JVP_HAS_KIND(key, JV_KIND_STRING)); + // copy/free of object, key, value coalesced +- jv* slot = jvp_object_write(&object, key); ++ jv* slot; ++ if (!jvp_object_write(&object, key, &slot)) { ++ jv_free(object); ++ return jv_invalid_with_msg(jv_string("Object too big")); ++ } + jv_free(*slot); + *slot = value; + return object; +@@ -1808,6 +1833,7 @@ jv jv_object_merge(jv a, jv b) { + assert(JVP_HAS_KIND(a, JV_KIND_OBJECT)); + jv_object_foreach(b, k, v) { + a = jv_object_set(a, k, v); ++ if (!jv_is_valid(a)) break; + } + jv_free(b); + return a; +@@ -1827,6 +1853,7 @@ jv jv_object_merge_recursive(jv a, jv b) { + jv_free(elem); + a = jv_object_set(a, k, v); + } ++ if (!jv_is_valid(a)) break; + } + jv_free(b); + return a; +diff --git a/src/jv_aux.c b/src/jv_aux.c +index 994285a..0753aef 100644 +--- a/src/jv_aux.c ++++ b/src/jv_aux.c +@@ -162,18 +162,19 @@ jv jv_set(jv t, jv k, jv v) { + if (slice_len < insert_len) { + // array is growing + int shift = insert_len - slice_len; +- for (int i = array_len - 1; i >= end; i--) { ++ for (int i = array_len - 1; i >= end && jv_is_valid(t); i--) { + t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); + } + } else if (slice_len > insert_len) { + // array is shrinking + int shift = slice_len - insert_len; +- for (int i = end; i < array_len; i++) { ++ for (int i = end; i < array_len && jv_is_valid(t); i++) { + t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); + } +- t = jv_array_slice(t, 0, array_len - shift); ++ if (jv_is_valid(t)) ++ t = jv_array_slice(t, 0, array_len - shift); + } +- for (int i=0; i < insert_len; i++) { ++ for (int i = 0; i < insert_len && jv_is_valid(t); i++) { + t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); + } + jv_free(v); +diff --git a/tests/jq.test b/tests/jq.test +index 2d5c36b..c6c6ee5 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -186,6 +186,10 @@ null + [0,1,2] + [0,5,2] + ++try (.[999999999] = 0) catch . ++null ++"Array index too large" ++ + # + # Multiple outputs, iteration + # +-- +2.49.0 + diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-01.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-01.patch new file mode 100644 index 0000000000..dbced0a1a3 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-01.patch @@ -0,0 +1,69 @@ +From 4240af6a20465894dce871707271e11a05432dac Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Sun, 16 Feb 2025 22:08:36 +0900 +Subject: [PATCH 1/2] fix: `jv_number_value` should cache the double value of + literal numbers (#3245) + +The code of `jv_number_value` is intended to cache the double value of +literal numbers, but it does not work because it accepts the `jv` struct +by value. This patch fixes the behavior by checking if the double value +is `NaN`, which indicates the unconverted value. This patch improves the +performance of major use cases; e.g. `range(1000000)` runs 25% faster. + +CVE: CVE-2024-53427 +Upstream-Status: Backport [https://github.com/jqlang/jq/commit/b86ff49f46a4a37e5a8e75a140cb5fd6e1331384] +Signed-off-by: Colin Pinnell McAllister +--- + src/jv.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/src/jv.c b/src/jv.c +index 4d7bba1..9051f65 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -209,9 +209,6 @@ enum { + JVP_NUMBER_DECIMAL = 1 + }; + +-#define JV_NUMBER_SIZE_INIT (0) +-#define JV_NUMBER_SIZE_CONVERTED (1) +- + #define JVP_FLAGS_NUMBER_NATIVE JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_NATIVE, 0)) + #define JVP_FLAGS_NUMBER_NATIVE_STR JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_NATIVE, 1)) + #define JVP_FLAGS_NUMBER_LITERAL JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_DECIMAL, 1)) +@@ -619,8 +616,12 @@ static jv jvp_literal_number_new(const char * literal) { + jv_mem_free(n); + return JV_INVALID; + } ++ if (decNumberIsNaN(&n->num_decimal)) { ++ jv_mem_free(n); ++ return jv_number(NAN); ++ } + +- jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; ++ jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, 0, {&n->refcnt}}; + return r; + } + +@@ -719,9 +720,8 @@ double jv_number_value(jv j) { + if (JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL)) { + jvp_literal_number* n = jvp_literal_number_ptr(j); + +- if (j.size != JV_NUMBER_SIZE_CONVERTED) { ++ if (isnan(n->num_double)) { + n->num_double = jvp_literal_number_to_double(j); +- j.size = JV_NUMBER_SIZE_CONVERTED; + } + + return n->num_double; +@@ -755,6 +755,7 @@ int jvp_number_is_nan(jv n) { + } else { + return n.u.number != n.u.number; + } ++ return isnan(n.u.number); + } + + int jvp_number_cmp(jv a, jv b) { +-- +2.49.0 + diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-02.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-02.patch new file mode 100644 index 0000000000..f650d28c85 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427-02.patch @@ -0,0 +1,56 @@ +From aea65caf03c129f3303d044044d2d1105be81b71 Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Wed, 5 Mar 2025 07:43:54 +0900 +Subject: [PATCH 2/2] Reject NaN with payload while parsing JSON + +This commit drops support for parsing NaN with payload in JSON like +`NaN123` and fixes CVE-2024-53427. Other JSON extensions like `NaN` and +`Infinity` are still supported. Fixes #3023, fixes #3196, fixes #3246. + +CVE: CVE-2024-53427 +Upstream-Status: Backport [https://github.com/jqlang/jq/commit/a09a4dfd55e6c24d04b35062ccfe4509748b1dd3] +Signed-off-by: Colin Pinnell McAllister +--- + src/jv.c | 5 +++++ + tests/jq.test | 12 ++++++++++++ + 2 files changed, 17 insertions(+) + +diff --git a/src/jv.c b/src/jv.c +index 9051f65..4da5ba8 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -617,6 +617,11 @@ static jv jvp_literal_number_new(const char * literal) { + return JV_INVALID; + } + if (decNumberIsNaN(&n->num_decimal)) { ++ // Reject NaN with payload. ++ if (n->num_decimal.digits > 1 || *n->num_decimal.lsu != 0) { ++ jv_mem_free(n); ++ return JV_INVALID; ++ } + jv_mem_free(n); + return jv_number(NAN); + } +diff --git a/tests/jq.test b/tests/jq.test +index f783493..0ab21ef 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -1724,3 +1724,15 @@ false + try 0[implode] catch . + [] + "Cannot index number with string \"\"" ++ ++# NaN with payload is not parsed ++.[] | try (fromjson | isnan) catch . ++["NaN","-NaN","NaN1","NaN10","NaN100","NaN1000","NaN10000","NaN100000"] ++true ++true ++"Invalid numeric literal at EOF at line 1, column 4 (while parsing 'NaN1')" ++"Invalid numeric literal at EOF at line 1, column 5 (while parsing 'NaN10')" ++"Invalid numeric literal at EOF at line 1, column 6 (while parsing 'NaN100')" ++"Invalid numeric literal at EOF at line 1, column 7 (while parsing 'NaN1000')" ++"Invalid numeric literal at EOF at line 1, column 8 (while parsing 'NaN10000')" ++"Invalid numeric literal at EOF at line 1, column 9 (while parsing 'NaN100000')" +-- +2.49.0 + diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch new file mode 100644 index 0000000000..909a4963c9 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch @@ -0,0 +1,46 @@ +From 9e23fd7e88bb2d76ddf3fbfc805199f848cd1b92 Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Sat, 31 May 2025 11:46:40 +0900 +Subject: [PATCH 2/2] Fix heap buffer overflow when formatting an empty string + +The `jv_string_empty` did not properly null-terminate the string data, +which could lead to a heap buffer overflow. The test case of +GHSA-p7rr-28xf-3m5w (`0[""*0]`) was fixed by the commit dc849e9bb74a, +but another case (`0[[]|implode]`) was still vulnerable. This commit +ensures string data is properly null-terminated, and fixes CVE-2025-48060. + +CVE: CVE-2025-48060 +Upstream-Status: Backport [https://github.com/jqlang/jq/commit/c6e041699d8cd31b97375a2596217aff2cfca85b] +Signed-off-by: Colin Pinnell McAllister +--- + src/jv.c | 1 + + tests/jq.test | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/src/jv.c b/src/jv.c +index 33ccee9..4d7bba1 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1131,6 +1131,7 @@ static jv jvp_string_empty_new(uint32_t length) { + jvp_string* s = jvp_string_alloc(length); + s->length_hashed = 0; + memset(s->data, 0, length); ++ s->data[length] = 0; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; + return r; + } +diff --git a/tests/jq.test b/tests/jq.test +index c6c6ee5..f783493 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -1720,3 +1720,7 @@ false + . |= try . catch . + 1 + 1 ++ ++try 0[implode] catch . ++[] ++"Cannot index number with string \"\"" +-- +2.49.0 + diff --git a/meta-oe/recipes-devtools/jq/jq_git.bb b/meta-oe/recipes-devtools/jq/jq_git.bb index 8b0218c83e..d36723cff4 100644 --- a/meta-oe/recipes-devtools/jq/jq_git.bb +++ b/meta-oe/recipes-devtools/jq/jq_git.bb @@ -9,7 +9,13 @@ LICENSE = "MIT" LIC_FILES_CHKSUM = "file://COPYING;md5=2814b59e00e7918c864fa3b6bbe049b4" PV = "1.6+git${SRCPV}" -SRC_URI = "git://github.com/stedolan/jq;protocol=https;branch=master" +SRC_URI = " \ + git://github.com/stedolan/jq;protocol=https;branch=master \ + file://CVE-2024-23337.patch \ + file://CVE-2025-48060.patch \ + file://CVE-2024-53427-01.patch \ + file://CVE-2024-53427-02.patch \ + " SRCREV = "a9f97e9e61a910a374a5d768244e8ad63f407d3e" S = "${WORKDIR}/git" -- cgit v1.2.3-54-g00ecf