Tighten string length bounds and propagate invalid jv in implode

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 <[email protected]>
This commit is contained in:
itchyny
2026-06-16 14:33:04 +09:00
co-authored by Dirk Müller
parent e987df0d46
commit 46d1da3094
2 changed files with 4 additions and 2 deletions
+1
View File
@@ -1434,6 +1434,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);
+3 -2
View File
@@ -1178,7 +1178,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"));
}
@@ -1379,7 +1379,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"));
}
@@ -1464,6 +1464,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);