From 9bd08aa09ea0cbd7b221aae9fc0534eb762d3de6 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 22 Jan 2013 16:53:52 +0200 Subject: [PATCH] lib-index: Fixes to handling broken cache records that point outside file. Especially try to avoid failing by trying to allocate gigabytes of memory. --- src/lib-index/mail-cache-lookup.c | 13 ++++++------- src/lib-index/mail-cache.c | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/lib-index/mail-cache-lookup.c b/src/lib-index/mail-cache-lookup.c index 1a9f2b1c8e..4b5b759850 100644 --- a/src/lib-index/mail-cache-lookup.c +++ b/src/lib-index/mail-cache-lookup.c @@ -15,6 +15,7 @@ int mail_cache_get_record(struct mail_cache *cache, uint32_t offset, { const struct mail_cache_record *rec; const void *data; + int ret; i_assert(offset != 0); @@ -41,17 +42,15 @@ int mail_cache_get_record(struct mail_cache *cache, uint32_t offset, } if (rec->size > CACHE_PREFETCH) { /* larger than we guessed. map the rest of the record. */ - if (mail_cache_map(cache, offset, rec->size, &data) < 0) + if ((ret = mail_cache_map(cache, offset, rec->size, &data)) < 0) + return -1; + if (ret == 0) { + mail_cache_set_corrupted(cache, "record points outside file"); return -1; + } rec = data; } - if (rec->size > cache->mmap_length || - offset + rec->size > cache->mmap_length) { - mail_cache_set_corrupted(cache, "record points outside file"); - return -1; - } - *rec_r = rec; return 0; } diff --git a/src/lib-index/mail-cache.c b/src/lib-index/mail-cache.c index da40232c49..49b36beddd 100644 --- a/src/lib-index/mail-cache.c +++ b/src/lib-index/mail-cache.c @@ -357,12 +357,28 @@ mail_cache_map_with_read(struct mail_cache *cache, size_t offset, size_t size, int mail_cache_map(struct mail_cache *cache, size_t offset, size_t size, const void **data_r) { + struct stat st; const void *data; ssize_t ret; if (size == 0) size = sizeof(struct mail_cache_header); + /* verify offset + size before trying to allocate a huge amount of + memory due to them. note that we may be prefetching more than we + actually need, so don't fail too early. */ + if (size > cache->mmap_length || offset + size > cache->mmap_length) { + if (fstat(cache->fd, &st) < 0) { + i_error("fstat(%s) failed: %m", cache->filepath); + return -1; + } + if (offset >= (uoff_t)st.st_size) { + *data_r = NULL; + return 0; + } + size = st.st_size - offset; + } + cache->remap_counter++; if (cache->map_with_read) return mail_cache_map_with_read(cache, offset, size, data_r); @@ -451,8 +467,7 @@ static int mail_cache_try_open(struct mail_cache *cache) mail_cache_init_file_cache(cache); - if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header), - &data) < 0) + if (mail_cache_map(cache, 0, 0, &data) < 0) return -1; return 1; } -- 2.47.3