From: Timo Sirainen Date: Sat, 30 Apr 2016 11:19:02 +0000 (+0300) Subject: lib-mail: message-parser assert-crashfix X-Git-Tag: 2.3.0.rc1~3879 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=12fd7a30563f3256642070ef9528dda0d089cb41;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: message-parser assert-crashfix Crashes when multipart MIME header is missing end-of-headers line and the boundary begins with the same prefix as one of the parent boundaries. Broken by 7a12331c6 --- diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c index 7f8e588d83..3a1aca7938 100644 --- a/src/lib-mail/message-parser.c +++ b/src/lib-mail/message-parser.c @@ -174,6 +174,8 @@ message_part_append(pool_t pool, struct message_part *parent) struct message_part *p, *part, **list; i_assert(parent != NULL); + i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART | + MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0); part = p_new(pool, struct message_part, 1); part->parent = parent; @@ -314,6 +316,8 @@ static int parse_part_finish(struct message_parser_ctx *ctx, struct message_part *part; size_t line_size; + i_assert(ctx->last_boundary == NULL); + /* get back to parent MIME part, summing the child MIME part sizes into parent's body sizes */ for (part = ctx->part; part != boundary->part; part = part->parent) { @@ -625,8 +629,8 @@ static int parse_next_header(struct message_parser_ctx *ctx, Content-Type. */ i_assert(!ctx->multipart); part->flags = 0; - ctx->last_boundary = NULL; } + ctx->last_boundary = NULL; if (!ctx->part_seen_content_type || (part->flags & MESSAGE_PART_FLAG_IS_MIME) == 0) { diff --git a/src/lib-mail/test-message-parser.c b/src/lib-mail/test-message-parser.c index d682fcfcce..3ea3907fd1 100644 --- a/src/lib-mail/test-message-parser.c +++ b/src/lib-mail/test-message-parser.c @@ -499,6 +499,81 @@ static const char input_msg[] = test_end(); } +static void test_message_parser_continuing_truncated_mime_boundary(void) +{ +static const char input_msg[] = +"Content-Type: multipart/mixed; boundary=\"a\"\n" +"\n" +"--a\n" +"Content-Type: multipart/mixed; boundary=\"ab\"\n" +"MIME-Version: 1.0\n" +"--ab\n" +"Content-Type: text/plain\n" +"\n" +"--ab--\n" +"--a--\n\n"; + struct message_parser_ctx *parser; + struct istream *input; + struct message_part *parts, *part; + struct message_block block; + pool_t pool; + int ret; + + test_begin("message parser continuing truncated mime boundary"); + pool = pool_alloconly_create("message parser", 10240); + input = test_istream_create(input_msg); + + parser = message_parser_init(pool, input, 0, 0); + while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; + test_assert(ret < 0); + message_parser_deinit(&parser, &parts); + + part = parts; + test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); + test_assert(part->header_size.lines == 2); + test_assert(part->header_size.physical_size == 45); + test_assert(part->header_size.virtual_size == 45+2); + test_assert(part->body_size.lines == 9); + test_assert(part->body_size.physical_size == 112); + test_assert(part->body_size.virtual_size == 112+9); + + part = parts->children; + test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME)); + test_assert(part->physical_pos == 49); + test_assert(part->header_size.lines == 1); + test_assert(part->header_size.physical_size == 45+17); + test_assert(part->header_size.virtual_size == 45+17+1); + test_assert(part->body_size.lines == 0); + test_assert(part->body_size.physical_size == 0); + test_assert(part->children == NULL); + + /* this will not be a child, since the header was truncated. I guess + we could make it, but it would complicate the message-parser even + more. */ + part = parts->children->next; + test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); + test_assert(part->physical_pos == 117); + test_assert(part->header_size.lines == 1); + test_assert(part->header_size.physical_size == 25); + test_assert(part->header_size.virtual_size == 25+1); + test_assert(part->body_size.lines == 0); + test_assert(part->body_size.physical_size == 0); + test_assert(part->children == NULL); + + part = parts->children->next->next; + test_assert(part->flags == (MESSAGE_PART_FLAG_TEXT | MESSAGE_PART_FLAG_IS_MIME)); + test_assert(part->header_size.lines == 0); + test_assert(part->header_size.physical_size == 0); + test_assert(part->body_size.lines == 0); + test_assert(part->body_size.physical_size == 0); + test_assert(part->children == NULL); + test_assert(part->next == NULL); + + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +} + static void test_message_parser_no_eoh(void) { static const char input_msg[] = "a:b\n"; @@ -537,6 +612,7 @@ int main(void) test_message_parser_duplicate_mime_boundary, test_message_parser_garbage_suffix_mime_boundary, test_message_parser_continuing_mime_boundary, + test_message_parser_continuing_truncated_mime_boundary, test_message_parser_no_eoh, NULL };