]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mbox: Added more (and earlier) detection for errors in mbox istreams.
authorTimo Sirainen <tss@iki.fi>
Wed, 19 Nov 2014 08:37:57 +0000 (17:37 +0900)
committerTimo Sirainen <tss@iki.fi>
Wed, 19 Nov 2014 08:37:57 +0000 (17:37 +0900)
src/lib-storage/index/mbox/istream-raw-mbox.c
src/lib-storage/index/mbox/istream-raw-mbox.h
src/lib-storage/index/mbox/mbox-mail.c
src/lib-storage/index/mbox/mbox-sync-parse.c
src/lib-storage/index/mbox/mbox-sync-private.h
src/lib-storage/index/mbox/mbox-sync-rewrite.c
src/lib-storage/index/mbox/mbox-sync.c

index d1572843c3e73773d798200c1740ecc5aab87ec2..e655e7a109b725075e312b1cbbc868573d6a067b 100644 (file)
@@ -506,7 +506,8 @@ uoff_t istream_raw_mbox_get_start_offset(struct istream *stream)
        return rstream->from_offset;
 }
 
-uoff_t istream_raw_mbox_get_header_offset(struct istream *stream)
+int istream_raw_mbox_get_header_offset(struct istream *stream,
+                                      uoff_t *hdr_offset_r)
 {
        struct raw_mbox_istream *rstream =
                (struct raw_mbox_istream *)stream->real_stream;
@@ -520,13 +521,17 @@ uoff_t istream_raw_mbox_get_header_offset(struct istream *stream)
                i_error("Unexpectedly lost From-line from mbox file %s at "
                        "%"PRIuUOFF_T, i_stream_get_name(stream),
                        rstream->from_offset);
-               return (uoff_t)-1;
+               return -1;
        }
+       if (stream->stream_errno != 0)
+               return -1;
 
-       return rstream->hdr_offset;
+       *hdr_offset_r = rstream->hdr_offset;
+       return 0;
 }
 
-uoff_t istream_raw_mbox_get_body_offset(struct istream *stream)
+int istream_raw_mbox_get_body_offset(struct istream *stream,
+                                    uoff_t *body_offset_r)
 {
        struct raw_mbox_istream *rstream =
                (struct raw_mbox_istream *)stream->real_stream;
@@ -534,8 +539,10 @@ uoff_t istream_raw_mbox_get_body_offset(struct istream *stream)
 
        i_assert(rstream->seeked);
 
-       if (rstream->body_offset != (uoff_t)-1)
-               return rstream->body_offset;
+       if (rstream->body_offset != (uoff_t)-1) {
+               *body_offset_r = rstream->body_offset;
+               return 0;
+       }
 
        offset = stream->v_offset;
        i_stream_seek(stream, rstream->hdr_offset);
@@ -551,27 +558,30 @@ uoff_t istream_raw_mbox_get_body_offset(struct istream *stream)
                        } else {
                                i_assert(rstream->body_offset != (uoff_t)-1);
                        }
-                       break;
+                       return -1;
                }
        }
 
        i_stream_seek(stream, offset);
-       return rstream->body_offset;
+       *body_offset_r = rstream->body_offset;
+       return 0;
 }
 
-uoff_t istream_raw_mbox_get_body_size(struct istream *stream,
-                                     uoff_t expected_body_size)
+int istream_raw_mbox_get_body_size(struct istream *stream,
+                                  uoff_t expected_body_size,
+                                  uoff_t *body_size_r)
 {
        struct raw_mbox_istream *rstream =
                (struct raw_mbox_istream *)stream->real_stream;
        const unsigned char *data;
        size_t size;
-       uoff_t old_offset, body_size, next_body_offset;
+       uoff_t old_offset, body_offset, body_size, next_body_offset;
 
        i_assert(rstream->seeked);
        i_assert(rstream->hdr_offset != (uoff_t)-1);
 
-       (void)istream_raw_mbox_get_body_offset(stream);
+       if (istream_raw_mbox_get_body_offset(stream, &body_offset) < 0)
+               return -1;
        body_size = rstream->mail_size == (uoff_t)-1 ? (uoff_t)-1 :
                rstream->mail_size - (rstream->body_offset -
                                      rstream->hdr_offset);
@@ -580,8 +590,10 @@ uoff_t istream_raw_mbox_get_body_size(struct istream *stream,
                /* if we already have the existing body size, use it as long as
                   it's >= expected body_size. otherwise the previous parsing
                   may have stopped at a From_-line that belongs to the body. */
-               if (body_size != (uoff_t)-1 && body_size >= expected_body_size)
-                       return body_size;
+               if (body_size != (uoff_t)-1 && body_size >= expected_body_size) {
+                       *body_size_r = body_size;
+                       return 0;
+               }
 
                next_body_offset = rstream->body_offset + expected_body_size;
                /* If header_missing_eoh is set, the message body begins with
@@ -600,21 +612,27 @@ uoff_t istream_raw_mbox_get_body_size(struct istream *stream,
                        rstream->mail_size =
                                next_body_offset - rstream->hdr_offset;
                        i_stream_seek(stream, old_offset);
-                       return expected_body_size;
+                       *body_size_r = expected_body_size;
+                       return 0;
                }
                /* invalid expected_body_size */
        }
-       if (body_size != (uoff_t)-1)
-               return body_size;
+       if (body_size != (uoff_t)-1) {
+               *body_size_r = body_size;
+               return 0;
+       }
 
        /* have to read through the message body */
        while (i_stream_read_data(stream, &data, &size, 0) > 0)
                i_stream_skip(stream, size);
        i_stream_seek(stream, old_offset);
+       if (stream->stream_errno != 0)
+               return -1;
 
        i_assert(rstream->mail_size != (uoff_t)-1);
-       return rstream->mail_size -
+       *body_size_r = rstream->mail_size -
                (rstream->body_offset - rstream->hdr_offset);
+       return 0;
 }
 
 time_t istream_raw_mbox_get_received_time(struct istream *stream)
@@ -651,13 +669,15 @@ bool istream_raw_mbox_has_crlf_ending(struct istream *stream)
        return rstream->crlf_ending;
 }
 
-void istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size)
+int istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size)
 {
        struct raw_mbox_istream *rstream =
                (struct raw_mbox_istream *)stream->real_stream;
        uoff_t body_size;
 
-       body_size = istream_raw_mbox_get_body_size(stream, expected_body_size);
+       if (istream_raw_mbox_get_body_size(stream, expected_body_size,
+                                          &body_size) < 0)
+               return -1;
        rstream->mail_size = (uoff_t)-1;
 
        rstream->received_time = rstream->next_received_time;
@@ -678,6 +698,7 @@ void istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size)
 
        rstream->eof = FALSE;
        rstream->istream.istream.eof = FALSE;
+       return 0;
 }
 
 int istream_raw_mbox_seek(struct istream *stream, uoff_t offset)
@@ -698,10 +719,8 @@ int istream_raw_mbox_seek(struct istream *stream, uoff_t offset)
        /* if seeked is FALSE, we unlocked in the middle. don't try to use
           any cached state then. */
        if (rstream->mail_size != (uoff_t)-1 && rstream->seeked &&
-           rstream->hdr_offset + rstream->mail_size == offset) {
-               istream_raw_mbox_next(stream, (uoff_t)-1);
-               return 0;
-       }
+           rstream->hdr_offset + rstream->mail_size == offset)
+               return istream_raw_mbox_next(stream, (uoff_t)-1);
 
        if (offset == rstream->from_offset && rstream->seeked) {
                /* back to beginning of current message */
index f9ce166f3946c556b6afa4e78e55d3226f35be5a..3d81b55aa33f3662dd1ec4141d1847846cfeeabf 100644 (file)
@@ -9,15 +9,18 @@ struct istream *i_stream_create_raw_mbox(struct istream *input);
 /* Return offset to beginning of the "\nFrom"-line. */
 uoff_t istream_raw_mbox_get_start_offset(struct istream *stream);
 /* Return offset to beginning of the headers. */
-uoff_t istream_raw_mbox_get_header_offset(struct istream *stream);
+int istream_raw_mbox_get_header_offset(struct istream *stream,
+                                      uoff_t *hdr_offset_r);
 /* Return offset to beginning of the body. */
-uoff_t istream_raw_mbox_get_body_offset(struct istream *stream);
+int istream_raw_mbox_get_body_offset(struct istream *stream,
+                                    uoff_t *body_offset_r);
 
 /* Return the number of bytes in the body of this message. If
    expected_body_size isn't (uoff_t)-1, we'll use it as potentially valid body
    size to avoid actually reading through the whole message. */
-uoff_t istream_raw_mbox_get_body_size(struct istream *stream,
-                                     uoff_t expected_body_size);
+int istream_raw_mbox_get_body_size(struct istream *stream,
+                                  uoff_t expected_body_size,
+                                  uoff_t *body_size_r);
 
 /* Return received time of current message, or (time_t)-1 if the timestamp is
    broken. */
@@ -30,7 +33,7 @@ bool istream_raw_mbox_has_crlf_ending(struct istream *stream);
 
 /* Jump to next message. If expected_body_size isn't (uoff_t)-1, we'll use it
    as potentially valid body size. */
-void istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size);
+int istream_raw_mbox_next(struct istream *stream, uoff_t expected_body_size);
 
 /* Seek to message at given offset. offset must point to beginning of
    "\nFrom ", or 0 for beginning of file. Returns -1 if it offset doesn't
index d18417f072ccc548dc919388b8f61bed8a17d2f7..8b5470eaa65cf5cf96574d5057cb04dafe332745 100644 (file)
@@ -304,10 +304,10 @@ static int mbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
                return -1;
 
        /* our header size varies, so don't do any caching */
-       body_offset = istream_raw_mbox_get_body_offset(mbox->mbox_stream);
-       if (body_offset == (uoff_t)-1) {
+       if (istream_raw_mbox_get_body_offset(mbox->mbox_stream, &body_offset) < 0) {
                mail_storage_set_critical(_mail->box->storage,
-                                         "Couldn't get mbox size");
+                       "mbox %s: Couldn't get body offset for uid=%u",
+                       mailbox_get_path(&mbox->box), mail->mail.mail.uid);
                return -1;
        }
 
@@ -319,8 +319,13 @@ static int mbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
                body_size = (uoff_t)-1;
 
        /* verify that the calculated body size is correct */
-       body_size = istream_raw_mbox_get_body_size(mbox->mbox_stream,
-                                                  body_size);
+       if (istream_raw_mbox_get_body_size(mbox->mbox_stream,
+                                          body_size, &body_size) < 0) {
+               mail_storage_set_critical(_mail->box->storage,
+                       "mbox %s: Couldn't get body size for uid=%u",
+                       mailbox_get_path(&mbox->box), mail->mail.mail.uid);
+               return -1;
+       }
 
        data->physical_size = hdr_size.physical_size + body_size;
        *size_r = data->physical_size;
@@ -352,7 +357,12 @@ static int mbox_mail_init_stream(struct index_mail *mail)
        }
 
        raw_stream = mbox->mbox_stream;
-       hdr_offset = istream_raw_mbox_get_header_offset(raw_stream);
+       if (istream_raw_mbox_get_header_offset(raw_stream, &hdr_offset) < 0) {
+               mail_storage_set_critical(mbox->box.storage,
+                       "mbox %s: Couldn't get header offset for uid=%u",
+                       mailbox_get_path(&mbox->box), mail->mail.mail.uid);
+               return -1;
+       }
        i_stream_seek(raw_stream, hdr_offset);
 
        if (next_offset != (uoff_t)-1)
index 7db3073880213c5205db60457c46a32b58ca3e75..9e03bf904d93930b4ab55204dbc9b96e8021606a 100644 (file)
@@ -453,8 +453,8 @@ static int mbox_sync_bsearch_header_func_cmp(const void *p1, const void *p2)
        return strcasecmp(key, func->header);
 }
 
-void mbox_sync_parse_next_mail(struct istream *input,
-                              struct mbox_sync_mail_context *ctx)
+int mbox_sync_parse_next_mail(struct istream *input,
+                             struct mbox_sync_mail_context *ctx)
 {
        struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
        struct message_header_parser_ctx *hdr_ctx;
@@ -545,6 +545,12 @@ void mbox_sync_parse_next_mail(struct istream *input,
        }
 
        ctx->body_offset = input->v_offset;
+       if (input->stream_errno != 0) {
+               mbox_sync_set_critical(ctx->sync_ctx, "read(%s) failed: %s",
+                       i_stream_get_name(input), i_stream_get_error(input));
+               return -1;
+       }
+       return 0;
 }
 
 bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox,
index 43fcd30b06037f4a812e2a680622b6d6b8ff1bc6..275afe9d7b411ac25f7180baa65c808940aa80db 100644 (file)
@@ -158,8 +158,8 @@ int mbox_sync_has_changed_full(struct mbox_mailbox *mbox, bool leave_dirty,
 void mbox_sync_set_critical(struct mbox_sync_context *sync_ctx,
                            const char *fmt, ...) ATTR_FORMAT(2, 3);
 
-void mbox_sync_parse_next_mail(struct istream *input,
-                              struct mbox_sync_mail_context *ctx);
+int mbox_sync_parse_next_mail(struct istream *input,
+                             struct mbox_sync_mail_context *ctx);
 bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox,
                                struct mail_index_view *view, uint32_t seq);
 
index 5caf6d3ff29d3957e344ade6e34972b09d716e78..1ac6d21f6f6b417c34a9643bac378dfda70e1122 100644 (file)
@@ -318,11 +318,11 @@ int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff)
        return 1;
 }
 
-static void mbox_sync_read_next(struct mbox_sync_context *sync_ctx,
-                               struct mbox_sync_mail_context *mail_ctx,
-                               struct mbox_sync_mail *mails,
-                               uint32_t seq, uint32_t idx,
-                               uoff_t expunged_space)
+static int mbox_sync_read_next(struct mbox_sync_context *sync_ctx,
+                              struct mbox_sync_mail_context *mail_ctx,
+                              struct mbox_sync_mail *mails,
+                              uint32_t seq, uint32_t idx,
+                              uoff_t expunged_space)
 {
        unsigned int first_mail_expunge_extra;
        uint32_t orig_next_uid;
@@ -332,8 +332,12 @@ static void mbox_sync_read_next(struct mbox_sync_context *sync_ctx,
        mail_ctx->seq = seq;
        mail_ctx->header = sync_ctx->header;
 
-       mail_ctx->mail.offset =
-               istream_raw_mbox_get_header_offset(sync_ctx->input);
+       if (istream_raw_mbox_get_header_offset(sync_ctx->input,
+                                              &mail_ctx->mail.offset) < 0) {
+               mbox_sync_set_critical(sync_ctx,
+                       "Couldn't get header offset for seq=%u", seq);
+               return -1;
+       }
        mail_ctx->mail.body_size = mails[idx].body_size;
 
        orig_next_uid = sync_ctx->next_uid;
@@ -361,7 +365,8 @@ static void mbox_sync_read_next(struct mbox_sync_context *sync_ctx,
                mails[idx].from_offset += first_mail_expunge_extra;
        }
 
-       mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
+       if (mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx) < 0)
+               return -1;
        i_assert(mail_ctx->mail.pseudo == mails[idx].pseudo);
 
        /* set next_uid back before updating the headers. this is important
@@ -381,6 +386,7 @@ static void mbox_sync_read_next(struct mbox_sync_context *sync_ctx,
                if (mail_ctx->have_eoh)
                        str_append_c(mail_ctx->header, '\n');
        }
+       return 0;
 }
 
 static int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx,
@@ -398,8 +404,9 @@ static int mbox_sync_read_and_move(struct mbox_sync_context *sync_ctx,
                if (mbox_sync_seek(sync_ctx, mails[idx].from_offset) < 0)
                        return -1;
 
-               mbox_sync_read_next(sync_ctx, &new_mail_ctx, mails, seq, idx,
-                                   expunged_space);
+               if (mbox_sync_read_next(sync_ctx, &new_mail_ctx, mails, seq, idx,
+                                       expunged_space) < 0)
+                       return -1;
                mail_ctx = &new_mail_ctx;
        } else {
                i_assert(seq == mail_ctx->seq);
index 769acd34e38787c45fded140ef53163ce2482c57..b0e60bb7920450d25b5bb3703d3e33507f54d45a 100644 (file)
@@ -131,8 +131,10 @@ static int
 mbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
                         struct mbox_sync_mail_context *mail_ctx)
 {
+       uoff_t offset;
+
        /* get EOF */
-       (void)istream_raw_mbox_get_header_offset(sync_ctx->input);
+       (void)istream_raw_mbox_get_header_offset(sync_ctx->input, &offset);
        if (istream_raw_mbox_is_eof(sync_ctx->input))
                return 0;
 
@@ -144,19 +146,27 @@ mbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx,
 
        mail_ctx->mail.from_offset =
                istream_raw_mbox_get_start_offset(sync_ctx->input);
-       mail_ctx->mail.offset =
-               istream_raw_mbox_get_header_offset(sync_ctx->input);
+       if (istream_raw_mbox_get_header_offset(sync_ctx->input, &mail_ctx->mail.offset) < 0) {
+               mbox_sync_set_critical(sync_ctx,
+                       "Couldn't get header offset for seq=%u", mail_ctx->seq);
+               return -1;
+       }
+
+       if (mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx) < 0)
+               return -1;
+       if (istream_raw_mbox_is_corrupted(sync_ctx->input))
+               return -1;
 
-       mbox_sync_parse_next_mail(sync_ctx->input, mail_ctx);
        i_assert(sync_ctx->input->v_offset != mail_ctx->mail.from_offset ||
                 sync_ctx->input->eof);
 
-       if (istream_raw_mbox_is_corrupted(sync_ctx->input))
+       if (istream_raw_mbox_get_body_size(sync_ctx->input,
+                                          mail_ctx->content_length,
+                                          &mail_ctx->mail.body_size) < 0) {
+               mbox_sync_set_critical(sync_ctx,
+                       "Couldn't get body size for seq=%u", mail_ctx->seq);
                return -1;
-
-       mail_ctx->mail.body_size =
-               istream_raw_mbox_get_body_size(sync_ctx->input,
-                                              mail_ctx->content_length);
+       }
        i_assert(mail_ctx->mail.body_size < OFF_T_MAX);
 
        if ((mail_ctx->mail.flags & MAIL_RECENT) != 0 &&
@@ -810,7 +820,7 @@ static int
 mbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq)
 {
        struct mbox_mailbox *mbox = sync_ctx->mbox;
-       uoff_t old_offset;
+       uoff_t old_offset, offset;
        uint32_t uid;
        int ret;
         bool deleted;
@@ -864,7 +874,11 @@ mbox_sync_seek_to_seq(struct mbox_sync_context *sync_ctx, uint32_t seq)
 
         sync_ctx->idx_seq = seq;
        sync_ctx->dest_first_mail = sync_ctx->seq == 0;
-        (void)istream_raw_mbox_get_body_offset(sync_ctx->input);
+       if (istream_raw_mbox_get_body_offset(sync_ctx->input, &offset) < 0) {
+               mbox_sync_set_critical(sync_ctx,
+                       "Message body offset lookup failed");
+               return -1;
+       }
        return 1;
 }
 
@@ -1149,8 +1163,9 @@ static int mbox_sync_loop(struct mbox_sync_context *sync_ctx,
                        sync_ctx->idx_seq++;
                }
 
-               istream_raw_mbox_next(sync_ctx->input,
-                                     mail_ctx->mail.body_size);
+               if (istream_raw_mbox_next(sync_ctx->input,
+                                         mail_ctx->mail.body_size) < 0)
+                       return -1;
                offset = istream_raw_mbox_get_start_offset(sync_ctx->input);
 
                if (sync_ctx->need_space_seq != 0) {