]> dgit.raspbian.org Git - dovecot.git/commitdiff
[PATCH 14/14] lib-mail: Limit total MIME parameter count per message to 200 000
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 17 Apr 2026 13:57:24 +0000 (15:57 +0200)
committerNoah Meyerhans <noahm@debian.org>
Wed, 16 Sep 2026 19:06:35 +0000 (15:06 -0400)
Content-Type and Content-Disposition parameters are both parsed through
parse_mime_parameters().  A message with many MIME parts each having many
parameters can accumulate millions of struct message_part_param entries.

Add remaining_mime_params to struct message_part_data_limits (combined
budget for both Content-Type and Content-Disposition), initialised to
MESSAGE_PART_DATA_MAX_TOTAL_MIME_PARAMS (200 000).  Cap params_count to the
remaining budget before allocating and deduct accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gbp-Pq: Name 0014-lib-mail-Limit-total-MIME-parameter-count-per-messag.patch

src/lib-mail/message-part-data.c
src/lib-mail/message-part-data.h

index 2eed994e45ca29154ebae15282e93ff2c3e86dc4..e0a3676aae81245f8b00a34ce4a8bd50e983aecf 100644 (file)
@@ -263,7 +263,8 @@ void message_part_envelope_parse_from_header(pool_t pool,
 static void
 parse_mime_parameters(struct rfc822_parser_context *parser,
        pool_t pool, const struct message_part_param **params_r,
-       unsigned int *params_count_r)
+       unsigned int *params_count_r,
+       struct message_part_data_limits *limits)
 {
        const char *const *results;
        struct message_part_param *params;
@@ -275,6 +276,10 @@ parse_mime_parameters(struct rfc822_parser_context *parser,
        i_assert((params_count % 2) == 0);
        params_count /= 2;
 
+       if (params_count > limits->remaining_mime_params)
+               params_count = limits->remaining_mime_params;
+       limits->remaining_mime_params -= params_count;
+
        if (params_count > 0) {
                params = p_new(pool, struct message_part_param, params_count);
                for (i = 0; i < params_count; i++) {
@@ -289,7 +294,8 @@ parse_mime_parameters(struct rfc822_parser_context *parser,
 
 static void
 parse_content_type(struct message_part_data *data,
-       pool_t pool, struct message_header_line *hdr)
+       pool_t pool, struct message_header_line *hdr,
+       struct message_part_data_limits *limits)
 {
        struct rfc822_parser_context parser;
        string_t *str;
@@ -332,7 +338,7 @@ parse_content_type(struct message_part_data *data,
 
        parse_mime_parameters(&parser, pool,
                &data->content_type_params,
-               &data->content_type_params_count);
+               &data->content_type_params_count, limits);
        rfc822_parser_deinit(&parser);
 }
 
@@ -357,7 +363,8 @@ parse_content_transfer_encoding(struct message_part_data *data,
 
 static void
 parse_content_disposition(struct message_part_data *data,
-       pool_t pool, struct message_header_line *hdr)
+       pool_t pool, struct message_header_line *hdr,
+       struct message_part_data_limits *limits)
 {
        struct rfc822_parser_context parser;
        string_t *str;
@@ -374,7 +381,7 @@ parse_content_disposition(struct message_part_data *data,
 
        parse_mime_parameters(&parser, pool,
                &data->content_disposition_params,
-               &data->content_disposition_params_count);
+               &data->content_disposition_params_count, limits);
        rfc822_parser_deinit(&parser);
 }
 
@@ -453,7 +460,7 @@ parse_content_header(struct message_part_data *data,
        case 't':
        case 'T':
                if (strcasecmp(name, "Type") == 0 && data->content_type == NULL)
-                       parse_content_type(data, pool, hdr);
+                       parse_content_type(data, pool, hdr, limits);
                else if (strcasecmp(name, "Transfer-Encoding") == 0 &&
                         data->content_transfer_encoding == NULL)
                        parse_content_transfer_encoding(data, pool, hdr);
@@ -482,7 +489,7 @@ parse_content_header(struct message_part_data *data,
                                                      hdr->full_value_len);
                else if (strcasecmp(name, "Disposition") == 0 &&
                         data->content_disposition_params == NULL)
-                       parse_content_disposition(data, pool, hdr);
+                       parse_content_disposition(data, pool, hdr, limits);
                break;
        }
 }
index 7f1f9c3e1fa558f10a1dae9bc4fc57103cc0814e..f439926c90ca958407c74cb800b59c263aff2985 100644 (file)
@@ -10,15 +10,19 @@ struct message_header_line;
 
 #define MESSAGE_PART_DATA_MAX_TOTAL_ADDRESSES 100000
 #define MESSAGE_PART_DATA_MAX_TOTAL_LANGUAGE_TAGS 100000
+/* Combined limit for Content-Type + Content-Disposition parameters */
+#define MESSAGE_PART_DATA_MAX_TOTAL_MIME_PARAMS 200000
 
 struct message_part_data_limits {
        unsigned int remaining_addresses;
        unsigned int remaining_language_tags;
+       unsigned int remaining_mime_params;
 };
 
 #define MESSAGE_PART_DATA_LIMITS_INIT \
        { MESSAGE_PART_DATA_MAX_TOTAL_ADDRESSES, \
-         MESSAGE_PART_DATA_MAX_TOTAL_LANGUAGE_TAGS }
+         MESSAGE_PART_DATA_MAX_TOTAL_LANGUAGE_TAGS, \
+         MESSAGE_PART_DATA_MAX_TOTAL_MIME_PARAMS }
 
 struct message_part_param {
        const char *name;