fix(p6-iconv): prevent convert buffer overflow; validate both directions; reset on error; more tests

This commit is contained in:
2026-09-10 17:49:29 +02:00
parent 7d6665633d
commit cac805b661
5 changed files with 304 additions and 14 deletions
+46
View File
@@ -51,6 +51,11 @@ static void test_iconv_spec_valid() {
EXPECT_FALSE(charset_spec_valid("utf-8,no-such-charset"));
EXPECT_FALSE(charset_spec_valid(",,,"));
EXPECT_FALSE(charset_spec_valid("utf-8,"));
/* A target charset whose conversion emits embedded NUL bytes would be
truncated by the C-string wire helpers; it must be rejected up front. */
EXPECT_FALSE(charset_spec_valid("utf-8,utf-16"));
EXPECT_FALSE(charset_spec_valid("utf-16"));
EXPECT_FALSE(charset_spec_valid("iso-8859-1,utf-16"));
}
/* --- one-shot conversion ------------------------------------------------ */
@@ -93,6 +98,45 @@ static void test_iconv_unrepresentable_fails() {
charset_conversion_close(conv);
}
/* A latin1 high-bit byte expands to two UTF-8 bytes. With exactly 16 high
* bytes the output is exactly cap = in_len + 16, so the final iconv call fills
* the buffer completely and a naive NUL-terminator write would overflow. */
static void test_iconv_exact_fill_no_overflow() {
char name[64];
strcpy(name, "dir/");
int n = 4;
for (int i = 0; i < 16; i++)
name[n++] = (char)(0x80 + i);
name[n] = '\0';
void* conv = charset_conversion_open("iso-8859-1", "utf-8");
EXPECT_NOT_NULL(conv);
char* out = charset_convert(conv, name, NULL);
EXPECT_NOT_NULL(out);
EXPECT_EQ_INT((int)strlen(out), n + 16);
charset_conversion_close(conv);
free(out);
}
/* Many high-bit bytes force the output buffer past its initial cap, exercising
* the E2BIG growth path (input partially consumed/produced before the grow). */
static void test_iconv_growth_expanding_name() {
char name[256];
strcpy(name, "dir/");
int n = 4;
for (int i = 0; i < 80; i++)
name[n++] = (char)(0x80 + (i % 0x80));
name[n] = '\0';
void* conv = charset_conversion_open("iso-8859-1", "utf-8");
EXPECT_NOT_NULL(conv);
char* out = charset_convert(conv, name, NULL);
EXPECT_NOT_NULL(out);
EXPECT_EQ_INT((int)strlen(out), n + 80);
charset_conversion_close(conv);
free(out);
}
/* --- process-wide wire conversion ---------------------------------------- */
static void test_iconv_wire_sender_converts_local_to_remote() {
@@ -164,6 +208,8 @@ void test_iconv() {
test_iconv_latin1_to_utf8();
test_iconv_invalid_sequence_fails();
test_iconv_unrepresentable_fails();
test_iconv_exact_fill_no_overflow();
test_iconv_growth_expanding_name();
test_iconv_wire_sender_converts_local_to_remote();
test_iconv_wire_receiver_converts_remote_to_local();
test_iconv_wire_disabled_passthrough();