From: =?utf-8?b?IkNoYW5nWmh1byBDaGVuICjpmbPmmIzlgKwpIg==?= Date: Sun, 25 May 2025 03:08:51 +0800 Subject: Fix signed integer overflow in jvp_array_write and jvp_object_rehash CVE: CVE-2024-23337 Upstream: https://sources.debian.org/src/jq/1.7.1-6/debian/patches/CVE-2024-23337.patch/ Signed-off-by: Thomas Perale --- 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 7f798d8..a8fbe48 100644 --- a/src/jv.c +++ b/src/jv.c @@ -997,6 +997,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); @@ -1016,6 +1021,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; @@ -1288,6 +1294,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++; } } @@ -1310,14 +1317,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("")); @@ -1335,8 +1345,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; } @@ -1610,10 +1622,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) { @@ -1775,7 +1796,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; @@ -1800,6 +1825,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; @@ -1819,6 +1845,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 7036df2..944f9da 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 #