]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: Limit the number of RFC2231 parameters that can be parsed
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 24 Feb 2026 11:11:14 +0000 (13:11 +0200)
committerRebaser <foobar@foobar>
Thu, 26 Mar 2026 08:41:12 +0000 (08:41 +0000)
This avoids excessive CPU usage especially in result_append().

src/lib-mail/rfc2231-parser.c
src/lib-mail/rfc822-parser.h
src/lib-mail/test-rfc2231-parser.c

index 504a7fdb848a66338c73a4f80585a5f04f20da04..eb859213a536f6da0483be86503a56c8cd293b21 100644 (file)
@@ -201,7 +201,7 @@ int rfc2231_parse(struct rfc822_parser_context *ctx,
        struct rfc2231_parameter rfc2231_param;
        const char *key, *p, *p2;
        string_t *str;
-       unsigned int i, j, count, next, next_idx;
+       unsigned int i, j, count, next, next_idx, params_count = 0;
        bool ok, broken = FALSE;
        const char *prev_replacement_str;
        int ret;
@@ -219,6 +219,8 @@ int rfc2231_parse(struct rfc822_parser_context *ctx,
        t_array_init(&rfc2231_params_arr, 8);
        str = t_str_new(64);
        while ((ret = rfc822_parse_content_param(ctx, &key, str)) != 0) {
+               if (++params_count > RFC2231_MAX_PARAMS)
+                       break;
                if (ret < 0) {
                        /* try to continue anyway.. */
                        broken = TRUE;
index 84e9bda67137cc14a2ee4149b9c058455b38a3d8..8d623871f764dc30e51ef6b4cf8e82507b436b79 100644 (file)
@@ -3,6 +3,11 @@
 
 #include "unichar.h"
 
+/* Maximum number of parameters to parse. After this the rest of the parameters
+   are skipped. This is to avoid excessive CPU usage that can be caused by
+   merging of these parameters. */
+#define RFC2231_MAX_PARAMS 128
+
 /* This can be used as a common NUL replacement character */
 #define RFC822_NUL_REPLACEMENT_STR UNICODE_REPLACEMENT_CHAR_UTF8
 
index 005d562dbcf92d4272803fe1dea4b959f231db1d..56f054699545c6a33cd3203705efc86244971496 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
 #include "rfc822-parser.h"
 #include "rfc2231-parser.h"
 #include "test-common.h"
@@ -238,6 +239,34 @@ static void test_rfc2231_parser_multi_encodings(void)
        run_test("rfc2231 parser invalid encoding", input, sizeof(input)-1, output);
 }
 
+static void test_rfc2231_parser_limits(void)
+{
+       string_t *input = t_str_new(1024);
+
+       test_begin("rfc2231 parser limits");
+       str_append(input, "; ");
+       for (unsigned int i = 0; i < 1100; i++)
+               str_printfa(input, "a%u=b%u; ", i, i);
+       struct rfc822_parser_context parser;
+       const char *const *result;
+       rfc822_parser_init(&parser, str_data(input), str_len(input), NULL);
+       test_assert(rfc2231_parse(&parser, &result) == 0);
+
+       unsigned int count = str_array_length(result);
+       test_assert(count == RFC2231_MAX_PARAMS * 2);
+       for (unsigned int i = 0; i < count; i += 2) {
+               str_truncate(input, 0);
+               str_printfa(input, "a%u", i / 2);
+               test_assert_strcmp_idx(result[i], str_c(input), i);
+
+               str_truncate(input, 0);
+               str_printfa(input, "b%u", i / 2);
+               test_assert_strcmp_idx(result[i + 1], str_c(input), i);
+       }
+       rfc822_parser_deinit(&parser);
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
@@ -253,6 +282,7 @@ int main(void)
                test_rfc2231_parser_encodings,
                test_rfc2231_parser_utf8_encoding,
                test_rfc2231_parser_multi_encodings,
+               test_rfc2231_parser_limits,
                NULL
        };
        return test_run(test_functions);