]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: Fix another potential assert-crash when parsing illegal charset translation...
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 20 Mar 2026 22:11:53 +0000 (00:11 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 23 Mar 2026 08:31:47 +0000 (08:31 +0000)
The fix in 110c19e44e95be6b6d2b09cf994ce5b502c8dd8c was incomplete.

src/lib-mail/message-decoder.c
src/lib-mail/test-message-decoder.c

index db602bf315d7c99b59a0e9110820df10efe16f19..b51a3aa57591ad0fc94af9626f43d9592ab462a6 100644 (file)
@@ -230,6 +230,15 @@ static void translation_buf_decode(struct message_decoder_context *ctx,
 
        if (trans_size > ctx->translation_size) {
                /* more input was processed */
+       } else if (trans_size == ctx->translation_size &&
+                  result == CHARSET_RET_INCOMPLETE_INPUT) {
+               /* We get here at least with ISO-2022-JP:
+                  1. Incomplete sequence to change encoding state
+                  2. The final byte, which is found to be invalid.
+                  3. iconv() passes through the first two bytes instead of
+                     failing it as invalid input. The new byte starts a new
+                     incomplete encoding state.
+                  Handle this the same as if more input was processed. */
        } else if (trans_size > 0) {
                /* We get here when:
                   1. Incomplete sequence has been sent to charset_to_utf()
index fcf2a4cfae19731177558f0ad13e9552d963d9ee..a16714fce49a11b034f439388cefd41fad32eba6 100644 (file)
@@ -139,6 +139,107 @@ static void test_message_decoder_partial_illegal_sequence(void)
        test_end();
 }
 
+static void test_message_decoder_partial_illegal_sequence2(void)
+{
+       struct message_decoder_context *ctx;
+       struct message_part part;
+       struct message_header_line hdr;
+       struct message_block input, output;
+
+       test_begin("message decoder partial illegal sequence 2");
+
+       i_zero(&part);
+       i_zero(&input);
+       memset(&output, 0xff, sizeof(output));
+       input.part = &part;
+
+       ctx = message_decoder_init(NULL, 0);
+
+       i_zero(&hdr);
+       hdr.name = "Content-Type";
+       hdr.name_len = strlen(hdr.name);
+       hdr.full_value = (const void *)"text/plain; charset=ISO-2022-JP";
+       hdr.full_value_len = strlen((const char *)hdr.full_value);
+       input.hdr = &hdr;
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       test_assert(output.size == 0);
+
+       input.hdr = NULL;
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+
+       /* Incomplete encoding state change */
+       input.data = (const void *)"\x1b$";
+       input.size = strlen((const char *)input.data);
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       test_assert(output.size == 0);
+
+       /* State sequence becomes invalid */
+       input.data = (const void *)"\x1b";
+       input.size = strlen((const char *)input.data);
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       /* The initial invalid sequences become passthrough output */
+       test_assert(output.size == 2);
+       test_assert(memcmp(output.data, "\x1b$", 2) == 0);
+
+       /* Finish a valid encoding state change */
+       input.data = (const void *)"$B";
+       input.size = strlen((const char *)input.data);
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       test_assert(output.size == 0);
+
+       message_decoder_deinit(&ctx);
+
+       test_end();
+}
+
+static void test_message_decoder_partial_illegal_sequence3(void)
+{
+       struct message_decoder_context *ctx;
+       struct message_part part;
+       struct message_header_line hdr;
+       struct message_block input, output;
+
+       test_begin("message decoder partial illegal sequence 3");
+
+       i_zero(&part);
+       i_zero(&input);
+       memset(&output, 0xff, sizeof(output));
+       input.part = &part;
+
+       ctx = message_decoder_init(NULL, 0);
+
+       i_zero(&hdr);
+       hdr.name = "Content-Type";
+       hdr.name_len = strlen(hdr.name);
+       hdr.full_value = (const void *)"text/plain; charset=ISO-2022-JP";
+       hdr.full_value_len = strlen((const char *)hdr.full_value);
+       input.hdr = &hdr;
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       test_assert(output.size == 0);
+
+       input.hdr = NULL;
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+
+       /* Incomplete encoding state change */
+       input.data = (const void *)"\x1b$";
+       input.size = strlen((const char *)input.data);
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       test_assert(output.size == 0);
+
+       /* Continue with an invalid state sequence, but which by itself
+          becomes a valid state change sequence. */
+       input.data = (const void *)"\x1b$B";
+       input.size = strlen((const char *)input.data);
+       test_assert(message_decoder_decode_next_block(ctx, &input, &output));
+       /* The initial invalid sequences become passthrough output */
+       test_assert(output.size == 2);
+       test_assert(memcmp(output.data, "\x1b$", 2) == 0);
+
+       message_decoder_deinit(&ctx);
+
+       test_end();
+}
+
 static void test_message_decoder_multipart(void)
 {
        static const char test_message_input[] =
@@ -609,6 +710,8 @@ int main(void)
        static void (*const test_functions[])(void) = {
                test_message_decoder,
                test_message_decoder_partial_illegal_sequence,
+               test_message_decoder_partial_illegal_sequence2,
+               test_message_decoder_partial_illegal_sequence3,
                test_message_decoder_multipart,
                test_message_decoder_current_content_type,
                test_message_decoder_content_transfer_encoding,