[PATCH] lib-imap: Fix imap_parser_params.list_count_limit to actually work
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 27 Apr 2026 14:40:46 +0000 (17:40 +0300)
committerNoah Meyerhans <noahm@debian.org>
Mon, 18 May 2026 20:03:51 +0000 (16:03 -0400)
The previous fix in d0f67b52914565a35f3817335ab9633cb291513c was
accidentally limiting the number of ')', not the number of '('.

Gbp-Pq: Name CVE-2026-42006.patch

src/lib-imap/imap-parser.c
src/lib-imap/test-imap-parser.c

index 6212aed33d85b217f2e183bc446c79c1824da739..b1df3d7b788355bd5538b2d30fc8bd7a35eb54a1 100644 (file)
@@ -191,8 +191,15 @@ static struct imap_arg *imap_arg_create(struct imap_parser *parser)
        return arg;
 }
 
-static void imap_parser_open_list(struct imap_parser *parser)
+static bool imap_parser_open_list(struct imap_parser *parser)
 {
+       if (parser->list_count >= parser->list_count_limit) {
+               parser->error_msg = "Too many '('";
+               parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
+               return FALSE;
+       }
+       parser->list_count++;
+
        parser->list_arg = imap_arg_create(parser);
        parser->list_arg->type = IMAP_ARG_LIST;
        p_array_init(&parser->list_arg->_data.list, parser->pool,
@@ -200,6 +207,7 @@ static void imap_parser_open_list(struct imap_parser *parser)
        parser->cur_list = &parser->list_arg->_data.list;
 
        parser->cur_type = ARG_PARSE_NONE;
+       return TRUE;
 }
 
 static bool imap_parser_close_list(struct imap_parser *parser)
@@ -217,12 +225,6 @@ static bool imap_parser_close_list(struct imap_parser *parser)
                parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
                return FALSE;
        }
-       if (parser->list_count >= parser->list_count_limit) {
-               parser->error_msg = "Too many '('";
-               parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
-               return FALSE;
-       }
-       parser->list_count++;
 
        arg = imap_arg_create(parser);
        arg->type = IMAP_ARG_EOL;
@@ -673,7 +675,8 @@ static bool imap_parser_read_arg(struct imap_parser *parser)
                        parser->literal8 = FALSE;
                        break;
                case '(':
-                       imap_parser_open_list(parser);
+                       if (!imap_parser_open_list(parser))
+                               return FALSE;
                        if ((parser->flags & IMAP_PARSE_FLAG_STOP_AT_LIST) != 0) {
                                i_stream_skip(parser->input, 1);
                                return FALSE;
index 0075de71f38a5ddbcdc48698769ba3b35093818b..e61a1d535669804d47454e4e6ba343c9a582cb2b 100644 (file)
@@ -85,9 +85,15 @@ static void test_imap_parser_list_limit(void)
        struct {
                const char *input;
                int ret;
+               const char *error;
        } tests[] = {
-               { "(())\r\n", 1 },
-               { "((()))\r\n", -1 },
+               { "(())\r\n", 1, NULL },
+               { "((\r\n", -1, "Missing ')'" },
+               { "(()\r\n", -1, "Missing ')'" },
+               { "(()))\r\n", -1, "Unexpected ')'" },
+               { "((()))\r\n", -1, "Too many '('" },
+               { "(({10}\r\n", -2, NULL },
+               { "((({10}\r\n", -1, "Too many '('" },
        };
        struct istream_chain *chain;
        struct istream *chain_input;
@@ -112,6 +118,10 @@ static void test_imap_parser_list_limit(void)
                        (void)i_stream_read(chain_input);
 
                        test_assert_cmp(imap_parser_read_args(parser, 0, 0, &args), ==, tests[i].ret);
+                       if (tests[i].ret == -1) {
+                               enum imap_parser_error err;
+                               test_assert_strcmp_idx(imap_parser_get_error(parser, &err), tests[i].error, i);
+                       }
                        /* skip over CRLF */
                        i_stream_skip(chain_input, i_stream_get_data_size(chain_input));