]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Fix potential assert-crash when adding missing attachment flags
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 30 Nov 2020 18:37:46 +0000 (20:37 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 8 Dec 2020 09:17:16 +0000 (09:17 +0000)
This happened only in a rather special condition. Added unit test to
reproduce it.

Fixes:
Panic: file index-mail-headers.c: line 298 (index_mail_parse_header): assertion failed: (part != NULL)

src/lib-storage/index/index-mail.c
src/lib-storage/test-mail.c

index 61e6bfb3d85ba35966420a30d1d670d045f96f40..a3629429108b47b04acec27eac327f528e5301f5 100644 (file)
@@ -1412,11 +1412,18 @@ static int index_mail_parse_bodystructure(struct index_mail *mail,
                        /* we haven't parsed the header yet */
                        const char *reason =
                                index_mail_cache_reason(&mail->mail.mail, "bodystructure");
+                       bool orig_bodystructure_header =
+                               data->save_bodystructure_header;
+                       bool orig_bodystructure_body =
+                               data->save_bodystructure_body;
                        data->save_bodystructure_header = TRUE;
                        data->save_bodystructure_body = TRUE;
                        (void)get_cached_parts(mail);
                        if (index_mail_parse_headers(mail, NULL, reason) < 0) {
-                               data->save_bodystructure_header = TRUE;
+                               data->save_bodystructure_header =
+                                       orig_bodystructure_header;
+                               data->save_bodystructure_body =
+                                       orig_bodystructure_body;
                                return -1;
                        }
                        i_assert(data->parser_ctx != NULL);
index 4fa30a367bb99c509c3a52017868effc7a3b9345..8b799cece35fd22efeb8912f0500283413a183de 100644 (file)
@@ -282,10 +282,23 @@ static void test_mail_random_fetch(struct mailbox *box, uint32_t seq)
 static void test_mail_random_access(void)
 {
        struct test_mail_storage_ctx *ctx;
+       const char *const potential_never_cache_fields[] = {
+               "",
+               "flags",
+               "mime.parts",
+               "imap.body",
+               "imap.bodystructure",
+       };
+       unsigned int never_cache_field_idx =
+               i_rand_limit(N_ELEMENTS(potential_never_cache_fields));
+       const char *never_cache_fields =
+               potential_never_cache_fields[never_cache_field_idx];
        struct test_mail_storage_settings set = {
                .driver = "sdbox",
                .extra_input = (const char *const[]) {
                        "mail_attachment_detection_options=add-flags",
+                       t_strconcat("mail_never_cache_fields=",
+                                   never_cache_fields, NULL),
                        NULL
                },
        };
@@ -294,6 +307,7 @@ static void test_mail_random_access(void)
        test_begin("mail");
        ctx = test_mail_storage_init();
        test_mail_storage_init_user(ctx, &set);
+       e_debug(test_event, "mail_never_cache_fields=%s", never_cache_fields);
        for (unsigned int i = 0; i < 20; i++) {
                box = mailbox_alloc(ctx->user->namespaces->list, "INBOX", 0);
                if (mailbox_open(box) < 0)
@@ -319,10 +333,71 @@ static void test_mail_random_access(void)
        test_end();
 }
 
+static void test_attachment_flags_during_header_fetch(void)
+{
+       struct test_mail_storage_ctx *ctx;
+       struct test_mail_storage_settings set = {
+               .driver = "sdbox",
+               .extra_input = (const char *const[]) {
+                       "mail_attachment_detection_options=add-flags",
+                       "mail_never_cache_fields=mime.parts",
+                       NULL
+               },
+       };
+
+       test_begin("mail attachment flags during header fetch");
+       ctx = test_mail_storage_init();
+       test_mail_storage_init_user(ctx, &set);
+
+       struct mailbox *box =
+               mailbox_alloc(ctx->user->namespaces->list, "INBOX", 0);
+       test_assert(mailbox_open(box) == 0);
+
+#define TEST_HDR_FROM "From: <test1@example.com>\r\n"
+       test_mail_save(box,
+                      TEST_HDR_FROM
+                      "\r\n"
+                      "test body\n");
+       /* Remove the $HasNoAttachment keyword */
+       test_mail_remove_keywords(box);
+
+       struct mailbox_transaction_context *trans =
+               mailbox_transaction_begin(box, 0, __func__);
+       struct mail *mail = mail_alloc(trans, 0, NULL);
+       mail_set_seq(mail, 1);
+
+       const char *from_headers[] = { "From", NULL };
+       struct mailbox_header_lookup_ctx *headers =
+               mailbox_header_lookup_init(box, from_headers);
+
+       struct istream *input;
+       const unsigned char *data;
+       size_t size;
+       test_assert(mail_get_header_stream(mail, headers, &input) == 0);
+       test_assert(i_stream_read_more(input, &data, &size) == 1);
+       /* TEST_HDR_FROM */
+       test_assert(size == strlen(TEST_HDR_FROM) &&
+                   memcmp(data, TEST_HDR_FROM, strlen(TEST_HDR_FROM)) == 0);
+       i_stream_skip(input, size);
+       test_assert(i_stream_read_more(input, &data, &size) == 1);
+       test_assert(size == 2 && memcmp(data, "\r\n", 2) == 0);
+       i_stream_skip(input, size);
+       test_assert(i_stream_read_more(input, &data, &size) == -1);
+
+       mailbox_header_lookup_unref(&headers);
+       mail_free(&mail);
+       test_assert(mailbox_transaction_commit(&trans) == 0);
+       mailbox_free(&box);
+       test_mail_storage_deinit_user(ctx);
+       test_mail_storage_deinit(&ctx);
+       test_end();
+}
+
 int main(int argc, char **argv)
 {
        void (*const tests[])(void) = {
                test_mail_random_access,
+               test_attachment_flags_during_header_fetch,
                NULL
        };
        int ret;