]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-index: Fix crash when cache record size is larger than file size
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 12 Mar 2021 00:26:32 +0000 (02:26 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Thu, 18 Mar 2021 14:05:44 +0000 (14:05 +0000)
This shouldn't happen unless the dovecot.index.cache file was corrupted.

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

index 07e6238f12a26935426ec6172c55ff80f737c160..af575e282d81cb48068b8f19138f8678cd698293 100644 (file)
@@ -405,6 +405,7 @@ mail_cache_map_full(struct mail_cache *cache, size_t offset, size_t size,
        struct stat st;
        const void *data;
        ssize_t ret;
+       size_t orig_size = size;
 
        *corrupted_r = FALSE;
 
@@ -465,6 +466,10 @@ mail_cache_map_full(struct mail_cache *cache, size_t offset, size_t size,
                /* already mapped */
                i_assert(cache->mmap_base != NULL);
                *data_r = CONST_PTR_OFFSET(cache->mmap_base, offset);
+               if (orig_size > cache->mmap_length - offset) {
+                       /* requested offset/size points outside file */
+                       return 0;
+               }
                return 1;
        }
 
@@ -500,7 +505,7 @@ mail_cache_map_full(struct mail_cache *cache, size_t offset, size_t size,
        }
        *data_r = offset > cache->mmap_length ? NULL :
                CONST_PTR_OFFSET(cache->mmap_base, offset);
-       return mail_cache_map_finish(cache, offset, size,
+       return mail_cache_map_finish(cache, offset, orig_size,
                                     cache->mmap_base, FALSE, corrupted_r);
 }
 
index e4e5503f62f020f1aa18a685ce6036e8f3bbd7c9..14b3fb62063943597bb36f23abc79991461aa3ec 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "str.h"
+#include "write-full.h"
 #include "test-common.h"
 #include "test-mail-cache.h"
 
@@ -712,6 +713,38 @@ static void test_mail_cache_in_memory(void)
        test_end();
 }
 
+static void test_mail_cache_size_corruption(void)
+{
+       struct test_mail_cache_ctx ctx;
+       struct mail_cache_view *cache_view;
+       struct mail_cache_lookup_iterate_ctx iter;
+       struct mail_cache_iterate_field field;
+
+       test_begin("mail cache size corruption");
+
+       test_mail_cache_init(test_mail_index_init(), &ctx);
+       test_mail_cache_add_mail(&ctx, ctx.cache_field.idx, "12345678");
+       cache_view = mail_cache_view_open(ctx.cache, ctx.view);
+
+       /* lookup the added cache field */
+       mail_cache_lookup_iter_init(cache_view, 1, &iter);
+       test_assert(iter.offset > 0);
+
+       uoff_t size_offset = iter.offset +
+               offsetof(struct mail_cache_record, size);
+       uint32_t new_size = 0x10000000;
+       test_assert(pwrite_full(ctx.cache->fd, &new_size, sizeof(new_size),
+                               size_offset) == 0);
+       test_expect_error_string("record points outside file");
+       test_assert(mail_cache_lookup_iter_next(&iter, &field) == -1);
+       test_expect_no_more_errors();
+
+       mail_cache_view_close(&cache_view);
+       test_mail_cache_deinit(&ctx);
+       test_mail_index_delete();
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
@@ -724,6 +757,7 @@ int main(void)
                test_mail_cache_lookup_decisions,
                test_mail_cache_lookup_decisions2,
                test_mail_cache_in_memory,
+               test_mail_cache_size_corruption,
                NULL
        };
        return test_run(test_functions);