From 3d03058fe2b2eacbd4ebe081032f85063599221c Mon Sep 17 00:00:00 2001 From: Roland Kovacs Date: Mon, 7 Jul 2025 08:38:09 +0200 Subject: jq-1.7.1: Backport multiple CVE fixes CVE: CVE-2024-23337 CVE: CVE-2024-53427 CVE: CVE-2025-48060 Patches CVE-2024-23337.patch and CVE-2024-53427.patch are backported from jq-1.8.0, and CVE-2025-48060.patch is backported from jq-1.8.1. Signed-off-by: Roland Kovacs Signed-off-by: Armin Kuster --- .../recipes-devtools/jq/jq/CVE-2024-23337.patch | 233 +++++++++++++++++++++ .../recipes-devtools/jq/jq/CVE-2024-53427.patch | 79 +++++++ .../recipes-devtools/jq/jq/CVE-2025-48060.patch | 45 ++++ meta-oe/recipes-devtools/jq/jq_1.7.1.bb | 3 + 4 files changed, 360 insertions(+) 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.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..93f55eecd0 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-23337.patch @@ -0,0 +1,233 @@ +From d9237e3d607f946fe74540efa42a2eacca2a6fbd Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Wed, 21 May 2025 07:45:00 +0900 +Subject: [PATCH] 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. + +(cherry picked from commit de21386681c0df0104a99d9d09db23a9b2a78b1e) +Signed-off-by: Roland Kovacs +--- + src/jv.c | 57 ++++++++++++++++++++++++++++++++++++++++----------- + src/jv_aux.c | 9 ++++---- + tests/jq.test | 4 ++++ + 3 files changed, 54 insertions(+), 16 deletions(-) + +diff --git a/src/jv.c b/src/jv.c +index 34573b8..15990f1 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1001,6 +1001,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); +@@ -1020,6 +1025,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; +@@ -1283,15 +1289,22 @@ jv jv_string_indexes(jv j, jv k) { + assert(JVP_HAS_KIND(k, JV_KIND_STRING)); + const char *jstr = jv_string_value(j); + const char *idxstr = jv_string_value(k); +- const char *p; ++ const char *p, *lp; + int jlen = jv_string_length_bytes(jv_copy(j)); + int idxlen = jv_string_length_bytes(jv_copy(k)); + jv a = jv_array(); + + if (idxlen != 0) { +- p = jstr; ++ int n = 0; ++ p = lp = jstr; + while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) { +- a = jv_array_append(a, jv_number(p - jstr)); ++ while (lp < p) { ++ lp += jvp_utf8_decode_length(*lp); ++ n++; ++ } ++ ++ a = jv_array_append(a, jv_number(n)); ++ if (!jv_is_valid(a)) break; + p++; + } + } +@@ -1314,14 +1327,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("")); +@@ -1339,8 +1355,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; + } +@@ -1614,10 +1632,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) { +@@ -1779,7 +1806,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; +@@ -1804,6 +1835,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; +@@ -1823,6 +1855,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 6004799..bbe1c0d 100644 +--- a/src/jv_aux.c ++++ b/src/jv_aux.c +@@ -193,18 +193,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 d052b22..22bfd3a 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -198,6 +198,10 @@ null + [0,1,2] + [0,5,2] + ++try (.[999999999] = 0) catch . ++null ++"Array index too large" ++ + # + # Multiple outputs, iteration + # diff --git a/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch new file mode 100644 index 0000000000..3e27a13036 --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2024-53427.patch @@ -0,0 +1,79 @@ +From fa6131eb6e9d43e88e35982fa5f6049da2a77a87 Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Wed, 5 Mar 2025 07:43:54 +0900 +Subject: [PATCH] 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. + +(cherry picked from commit a09a4dfd55e6c24d04b35062ccfe4509748b1dd3) +Signed-off-by: Roland Kovacs +--- + src/jv.c | 9 +++++++++ + tests/jq.test | 14 ++++++++++---- + tests/shtest | 5 ----- + 3 files changed, 19 insertions(+), 9 deletions(-) + +diff --git a/src/jv.c b/src/jv.c +index e23d8ec..34573b8 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -589,6 +589,15 @@ static jv jvp_literal_number_new(const char * literal) { + jv_mem_free(n); + 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); ++ } + + jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; + return r; +diff --git a/tests/jq.test b/tests/jq.test +index 7036df2..d052b22 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -1938,11 +1938,17 @@ tojson | fromjson + {"a":nan} + {"a":null} + +-# also "nan with payload" #2985 +-fromjson | isnan +-"nan1234" ++# 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')" + + # calling input/0, or debug/0 in a test doesn't crash jq + +diff --git a/tests/shtest b/tests/shtest +index 14aafbf..a471889 100755 +--- a/tests/shtest ++++ b/tests/shtest +@@ -594,11 +594,6 @@ if ! x=$($JQ -n "1 # foo$cr + 2") || [ "$x" != 1 ]; then + exit 1 + fi + +-# CVE-2023-50268: No stack overflow comparing a nan with a large payload +-$VALGRIND $Q $JQ '1 != .' <<\EOF >/dev/null +-Nan4000 +-EOF +- + # Allow passing the inline jq script before -- #2919 + if ! r=$($JQ --args -rn -- '$ARGS.positional[0]' bar) || [ "$r" != bar ]; then + echo "passing the inline script after -- didn't work" 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..237a50413f --- /dev/null +++ b/meta-oe/recipes-devtools/jq/jq/CVE-2025-48060.patch @@ -0,0 +1,45 @@ +From 35c08446e4bcd89e0e87e7750c68306d6c0e9ec5 Mon Sep 17 00:00:00 2001 +From: itchyny +Date: Sat, 31 May 2025 11:46:40 +0900 +Subject: [PATCH] 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. + +(cherry picked from commit c6e041699d8cd31b97375a2596217aff2cfca85b) +Signed-off-by: Roland Kovacs +--- + src/jv.c | 1 + + tests/jq.test | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/src/jv.c b/src/jv.c +index 15990f1..18dbb54 100644 +--- a/src/jv.c ++++ b/src/jv.c +@@ -1125,6 +1125,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 22bfd3a..ecb9116 100644 +--- a/tests/jq.test ++++ b/tests/jq.test +@@ -2030,6 +2030,10 @@ map(try implode catch .) + [123,["a"],[nan]] + ["implode input must be an array","string (\"a\") can't be imploded, unicode codepoint needs to be numeric","number (null) can't be imploded, unicode codepoint needs to be numeric"] + ++try 0[implode] catch . ++[] ++"Cannot index number with string \"\"" ++ + # walk + walk(.) + {"x":0} diff --git a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb index 6b12335513..9238474319 100644 --- a/meta-oe/recipes-devtools/jq/jq_1.7.1.bb +++ b/meta-oe/recipes-devtools/jq/jq_1.7.1.bb @@ -11,6 +11,9 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=488f4e0b04c0456337fb70d1ac1758ba" GITHUB_BASE_URI = "https://github.com/jqlang/${BPN}/releases/" SRC_URI = "${GITHUB_BASE_URI}/download/${BPN}-${PV}/${BPN}-${PV}.tar.gz \ file://run-ptest \ + file://CVE-2024-23337.patch \ + file://CVE-2024-53427.patch \ + file://CVE-2025-48060.patch \ " SRC_URI[sha256sum] = "478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2" -- cgit v1.2.3-54-g00ecf