]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Fix potential assert-crash if filter-stream fails
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 19 Aug 2021 10:32:45 +0000 (12:32 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 1 Sep 2021 09:07:00 +0000 (12:07 +0300)
If filter-stream isn't read until header (because the parent istream fails),
the header parsing isn't deinitialized. If after the failure the headers are
attempted to be parsed again, there's an assert-crash. Make sure this won't
happen by finishing the filter-istream read, and if that fails then reset the
header parsing anyway.

Destroying the filter_stream may also change the parent istream offset to
change, so this commit adds an extra seek to beginning of the istream when
beginning to parse the mail headers.

Fixes:
Panic: file index-mail-headers.c: line 198 (index_mail_parse_header_init): assertion failed: (!mail->data.header_parser_initialized)

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

index 80d3401a816eca83af3f14fdddc5d8146267efd5..59f1e6bbde6f41990e78e89ef8526fc390477e79 100644 (file)
@@ -22,6 +22,8 @@ static const struct message_parser_settings msg_parser_set = {
        .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
 };
 
+static void index_mail_filter_stream_destroy(struct index_mail *mail);
+
 static int header_line_cmp(const struct index_mail_line *l1,
                           const struct index_mail_line *l2)
 {
@@ -32,6 +34,11 @@ static int header_line_cmp(const struct index_mail_line *l1,
                (int)l1->line_num - (int)l2->line_num;
 }
 
+static void index_mail_parse_header_deinit(struct index_mail *mail)
+{
+       mail->data.header_parser_initialized = FALSE;
+}
+
 static void index_mail_parse_header_finish(struct index_mail *mail)
 {
        struct mail *_mail = &mail->mail.mail;
@@ -134,7 +141,7 @@ static void index_mail_parse_header_finish(struct index_mail *mail)
        }
 
        mail->data.dont_cache_field_idx = UINT_MAX;
-       mail->data.header_parser_initialized = FALSE;
+       index_mail_parse_header_deinit(mail);
 }
 
 static unsigned int
@@ -195,6 +202,7 @@ void index_mail_parse_header_init(struct index_mail *mail,
        const uint8_t *match;
        unsigned int i, field_idx, match_count;
 
+       index_mail_filter_stream_destroy(mail);
        i_assert(!mail->data.header_parser_initialized);
 
        mail->header_seq = data->seq;
@@ -425,6 +433,8 @@ static void index_mail_init_parser(struct index_mail *mail)
                }
        }
 
+       /* make sure parsing starts from the beginning of the stream */
+       i_stream_seek(mail->data.stream, 0);
        if (data->parts == NULL) {
                data->parser_input = data->stream;
                data->parser_ctx = message_parser_init(mail->mail.data_pool,
@@ -900,6 +910,11 @@ static void index_mail_filter_stream_destroy(struct index_mail *mail)
           header_parser_initialized=FALSE so we don't assert on it. */
        while (i_stream_read_more(mail->data.filter_stream, &data, &size) > 0)
                i_stream_skip(mail->data.filter_stream, size);
+       if (mail->data.header_parser_initialized) {
+               /* istream failed while reading the header */
+               i_assert(mail->data.filter_stream->stream_errno != 0);
+               index_mail_parse_header_deinit(mail);
+       }
        i_stream_destroy(&mail->data.filter_stream);
 }