]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-index: Fix detecting overly large mail_index_header.messages_count
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 29 Jan 2026 11:33:14 +0000 (13:33 +0200)
committerRebaser <foobar@foobar>
Thu, 26 Mar 2026 08:41:12 +0000 (08:41 +0000)
The previous check overflowed the integer calculation, which prevented the
check from working correctly.

This commit also removes the unnecessary
mail_index_record_map.mmap_used_size.

src/lib-index/mail-index-map-read.c
src/lib-index/mail-index-private.h
src/lib-index/test-mail-index.c

index e4f9675804978b875bd17ed3684c34079117cf1f..8695fefc6f7eb25fc2a5a6395d185bbcde0d9411 100644 (file)
@@ -82,17 +82,14 @@ static int mail_index_mmap(struct mail_index_map *map, uoff_t file_size)
        if (!mail_index_hdr_check_indexid(index, hdr))
                return -1;
 
-       rec_map->mmap_used_size = hdr->header_size +
-               hdr->messages_count * hdr->record_size;
+       /* header_size was checked by mail_index_check_header_compat() */
+       i_assert(hdr->header_size <= rec_map->mmap_size);
+       rec_map->records_count = (rec_map->mmap_size - hdr->header_size) /
+               hdr->record_size;
 
-       if (rec_map->mmap_used_size <= rec_map->mmap_size)
+       if (hdr->messages_count <= rec_map->records_count)
                rec_map->records_count = hdr->messages_count;
        else {
-               rec_map->records_count =
-                       (rec_map->mmap_size - hdr->header_size) /
-                       hdr->record_size;
-               rec_map->mmap_used_size = hdr->header_size +
-                       rec_map->records_count * hdr->record_size;
                mail_index_set_error(index, "Corrupted index file %s: "
                                     "messages_count too large (%u > %u)",
                                     index->filepath, hdr->messages_count,
index 2f5e1281118d3601a338c632d06237a84958edea..69ff0ae89ff604c81db79ec33e4b61e0657a17ee 100644 (file)
@@ -125,7 +125,7 @@ struct mail_index_record_map {
        ARRAY(struct mail_index_map *) maps;
 
        void *mmap_base;
-       size_t mmap_size, mmap_used_size;
+       size_t mmap_size;
 
        buffer_t *buffer;
 
index 49001ced98692bdd273a2d9c8e16a3203cfafcbf..e99c84f8d00d8d6de6f4cd5958a4c94b156077b8 100644 (file)
@@ -158,11 +158,66 @@ static void test_mail_index_new_extension(void)
        test_end();
 }
 
+static void test_mail_index_corruption_message_count(void)
+{
+       struct mail_index *index;
+       struct mail_index_view *view;
+       struct mail_index_transaction *trans;
+
+       test_begin("mail index corruption: message count");
+       index = test_mail_index_init(TRUE);
+
+       /* make dovecot.index at least MAIL_INDEX_MMAP_MIN_SIZE bytes */
+       view = mail_index_view_open(index);
+       trans = mail_index_transaction_begin(view,
+                       MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
+
+       uint32_t uid_validity = 1234;
+       mail_index_update_header(trans,
+                                offsetof(struct mail_index_header, uid_validity),
+                                &uid_validity, sizeof(uid_validity), TRUE);
+
+       uint32_t seq;
+       for (uint32_t uid = 1; uid <= 10000; uid++)
+               mail_index_append(trans, uid, &seq);
+       test_assert(mail_index_transaction_commit(&trans) == 0);
+       mail_index_view_close(&view);
+
+       /* write dovecot.index */
+       struct mail_index_sync_ctx *sync_ctx;
+       test_assert(mail_index_sync_begin(index, &sync_ctx, &view, &trans, 0) > 0);
+       mail_index_write(index, FALSE, "test");
+       test_assert(mail_index_sync_commit(&sync_ctx) == 0);
+
+       test_mail_index_close(&index);
+
+       /* write a corrupted messages_count */
+       const char *path = t_strconcat(test_mail_index_get_dir(),
+                                      "/test.dovecot.index", NULL);
+       int fd = open(path, O_RDWR);
+       if (fd == -1)
+               i_fatal("open(%s) failed: %m", path);
+
+       uint32_t messages_count = 0x80000000;
+       test_assert(pwrite(fd, &messages_count, sizeof(messages_count),
+                          offsetof(struct mail_index_header, messages_count)) ==
+                   sizeof(messages_count));
+       i_close_fd(&fd);
+
+       test_expect_error_string_n_times("test.dovecot.index", 3);
+       index = test_mail_index_open(FALSE);
+       test_expect_no_more_errors();
+       test_mail_index_deinit(&index);
+
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
                test_mail_index_rotate,
                test_mail_index_new_extension,
+               test_mail_index_corruption_message_count,
                NULL
        };
        test_dir_init("mail-index");