diff --git a/TODO b/TODO index c2b253bb95b4a88f96533c782fc07c4b8bc96549..16665916a2222612612d802b82b07b2c608167f5 100644 --- a/TODO +++ b/TODO @@ -12,10 +12,9 @@ Next (in order of descending priority/precedence): * Code cleanup * Full security review. Enforce limits on number of created objects, script size, execution time, etc... - - Limit the string size - - Limit the string list size - Limit the depth of the AST, i.e. command block and test list nesting. + - Make sure error messages don't print large erroneous values. - Limit the maximum number of included scripts - Make (configurable) limit on the number of redirects - Implement limits recommended by the variables RFC (5229) diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 67bacbce09aa034931da4762c33cf632e5e3e3e7..be5d923c8a9f6e6b0a74d39dfe9a2195e334ead5 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -89,6 +89,7 @@ libsieve_la_SOURCES = \ noinst_HEADERS = \ sieve-types.h \ sieve-common.h \ + sieve-limits.h \ sieve-message.h \ sieve-lexer.h \ sieve-script.h \ diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c index 08a882c721bf1ee59ef558a2a4589f031cf39e5a..5dbd3cab7821f6fa7406030e4fb9f228f8a64802 100644 --- a/src/lib-sieve/cmd-redirect.c +++ b/src/lib-sieve/cmd-redirect.c @@ -132,7 +132,7 @@ static bool cmd_redirect_validate if ( norm_address == NULL ) { sieve_command_validate_error(validator, cmd, "specified redirect address '%s' is invalid: %s", - str_c(address), error); + str_sanitize(str_c(address),256), error); } else { /* Replace string literal in AST */ sieve_ast_argument_string_setc(arg, norm_address); diff --git a/src/lib-sieve/sieve-address.c b/src/lib-sieve/sieve-address.c index 063f4a3bcd4c1592952eeaa546bb887225f243cd..a4f1b74299cb533ef77b4358e64a94b348d424af 100644 --- a/src/lib-sieve/sieve-address.c +++ b/src/lib-sieve/sieve-address.c @@ -1,5 +1,6 @@ #include "lib.h" #include "str.h" +#include "str-sanitize.h" #include "rfc822-parser.h" #include "sieve-common.h" @@ -132,7 +133,7 @@ static int parse_addr_spec(struct sieve_message_address_parser *ctx) } sieve_address_error(ctx, "invalid or lonely local part '%s' (expecting '@')", - str_c(ctx->local_part)); + str_sanitize(str_c(ctx->local_part), 80)); return -1; } diff --git a/src/lib-sieve/sieve-ast.c b/src/lib-sieve/sieve-ast.c index 6f3a48f8363396f1a151c6bb7fc0d28dd00ed288..d21653f38cea279c7fc8afe7fa80a37f53b8a9cf 100644 --- a/src/lib-sieve/sieve-ast.c +++ b/src/lib-sieve/sieve-ast.c @@ -173,6 +173,9 @@ void sieve_ast_error } #define __LIST_ADD(list, node) { \ + if ( list->len + 1 < list->len ) \ + return FALSE; \ + \ node->next = NULL; \ if ( list->head == NULL ) { \ node->prev = NULL; \ @@ -185,9 +188,13 @@ void sieve_ast_error } \ list->len++; \ node->list = list; \ + return TRUE; \ } #define __LIST_INSERT(list, before, node) { \ + if ( list->len + 1 < list->len ) \ + return FALSE; \ + \ node->next = before; \ if ( list->head == before ) { \ node->prev = NULL; \ @@ -199,6 +206,8 @@ void sieve_ast_error before->prev = node; \ list->len++; \ node->list = list; \ + \ + return TRUE; \ } @@ -206,7 +215,7 @@ void sieve_ast_error static struct sieve_ast_list *sieve_ast_list_create(pool_t pool) __LIST_CREATE(pool, struct sieve_ast_list) -static void sieve_ast_list_add +static bool sieve_ast_list_add (struct sieve_ast_list *list, struct sieve_ast_node *node) __LIST_ADD(list, node) @@ -214,11 +223,11 @@ static void sieve_ast_list_add struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool) __LIST_CREATE(pool, struct sieve_ast_arg_list) -void sieve_ast_arg_list_add +bool sieve_ast_arg_list_add (struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument) __LIST_ADD(list, argument) -void sieve_ast_arg_list_insert +bool sieve_ast_arg_list_insert (struct sieve_ast_arg_list *list, struct sieve_ast_argument *before, struct sieve_ast_argument *argument) __LIST_INSERT(list, before, argument) @@ -313,34 +322,34 @@ static struct sieve_ast_node *sieve_ast_node_create return node; } -static void sieve_ast_node_add_command +static bool sieve_ast_node_add_command (struct sieve_ast_node *node, struct sieve_ast_node *command) { i_assert( command->type == SAT_COMMAND && (node->type == SAT_ROOT || node->type == SAT_COMMAND) ); if (node->commands == NULL) node->commands = sieve_ast_list_create(node->ast->pool); - sieve_ast_list_add(node->commands, command); + return sieve_ast_list_add(node->commands, command); } -static void sieve_ast_node_add_test +static bool sieve_ast_node_add_test (struct sieve_ast_node *node, struct sieve_ast_node *test) { i_assert( test->type == SAT_TEST && (node->type == SAT_TEST || node->type == SAT_COMMAND) ); if (node->tests == NULL) node->tests = sieve_ast_list_create(node->ast->pool); - sieve_ast_list_add(node->tests, test); + return sieve_ast_list_add(node->tests, test); } -static void sieve_ast_node_add_argument +static bool sieve_ast_node_add_argument (struct sieve_ast_node *node, struct sieve_ast_argument *argument) { i_assert( node->type == SAT_TEST || node->type == SAT_COMMAND ); if (node->arguments == NULL) node->arguments = sieve_ast_arg_list_create(node->ast->pool); - sieve_ast_arg_list_add(node->arguments, argument); + return sieve_ast_arg_list_add(node->arguments, argument); } static void sieve_ast_argument_substitute @@ -431,7 +440,7 @@ struct sieve_ast_argument *sieve_ast_argument_stringlist_substitute return argument; } -static void _sieve_ast_stringlist_add +static bool _sieve_ast_stringlist_add (struct sieve_ast_argument *list, string_t *str, unsigned int source_line) { struct sieve_ast_argument *stritem; @@ -448,25 +457,25 @@ static void _sieve_ast_stringlist_add /* Clone string */ stritem->_value.str = str; - sieve_ast_arg_list_add(list->_value.strlist, stritem); + return sieve_ast_arg_list_add(list->_value.strlist, stritem); } -void sieve_ast_stringlist_add +bool sieve_ast_stringlist_add (struct sieve_ast_argument *list, const string_t *str, unsigned int source_line) { string_t *copied_str = str_new(list->ast->pool, str_len(str)); str_append_str(copied_str, str); - _sieve_ast_stringlist_add(list, copied_str, source_line); + return _sieve_ast_stringlist_add(list, copied_str, source_line); } -void sieve_ast_stringlist_add_strc +bool sieve_ast_stringlist_add_strc (struct sieve_ast_argument *list, const char *str, unsigned int source_line) { string_t *copied_str = str_new(list->ast->pool, strlen(str)); str_append(copied_str, str); - _sieve_ast_stringlist_add(list, copied_str, source_line); + return _sieve_ast_stringlist_add(list, copied_str, source_line); } struct sieve_ast_argument *sieve_ast_argument_tag_create @@ -478,7 +487,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_create argument->type = SAAT_TAG; argument->_value.tag = p_strdup(node->ast->pool, tag); - sieve_ast_node_add_argument(node, argument); + if ( !sieve_ast_node_add_argument(node, argument) ) + return NULL; return argument; } @@ -492,7 +502,8 @@ struct sieve_ast_argument *sieve_ast_argument_tag_insert argument->type = SAAT_TAG; argument->_value.tag = p_strdup(before->ast->pool, tag); - sieve_ast_arg_list_insert(before->list, before, argument); + if ( !sieve_ast_arg_list_insert(before->list, before, argument) ) + return NULL; return argument; } @@ -507,7 +518,8 @@ struct sieve_ast_argument *sieve_ast_argument_number_create argument->type = SAAT_NUMBER; argument->_value.number = number; - sieve_ast_node_add_argument(node, argument); + if ( !sieve_ast_node_add_argument(node, argument) ) + return NULL; return argument; } @@ -541,7 +553,8 @@ struct sieve_ast_node *sieve_ast_test_create test->identifier = p_strdup(parent->ast->pool, identifier); - sieve_ast_node_add_test(parent, test); + if ( !sieve_ast_node_add_test(parent, test) ) + return NULL; return test; } @@ -556,7 +569,8 @@ struct sieve_ast_node *sieve_ast_command_create command->identifier = p_strdup(parent->ast->pool, identifier); - sieve_ast_node_add_command(parent, command); + if ( !sieve_ast_node_add_command(parent, command) ) + return NULL; return command; } diff --git a/src/lib-sieve/sieve-ast.h b/src/lib-sieve/sieve-ast.h index 36c5d3cca8ff4b351ac969ae05189725deddff8d..b96261258c1799af53324b842cbc77d57e9cf4bd 100644 --- a/src/lib-sieve/sieve-ast.h +++ b/src/lib-sieve/sieve-ast.h @@ -185,9 +185,9 @@ struct sieve_ast_argument *sieve_ast_argument_create (struct sieve_ast *ast, unsigned int source_line); struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool); -void sieve_ast_arg_list_add +bool sieve_ast_arg_list_add (struct sieve_ast_arg_list *list, struct sieve_ast_argument *argument); -void sieve_ast_arg_list_insert +bool sieve_ast_arg_list_insert (struct sieve_ast_arg_list *list, struct sieve_ast_argument *before, struct sieve_ast_argument *argument); void sieve_ast_arg_list_substitute @@ -221,10 +221,10 @@ const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type); #define sieve_ast_argument_name(argument) \ sieve_ast_argument_type_name((argument)->type) -void sieve_ast_stringlist_add +bool sieve_ast_stringlist_add (struct sieve_ast_argument *list, const string_t *str, unsigned int source_line); -void sieve_ast_stringlist_add_strc +bool sieve_ast_stringlist_add_strc (struct sieve_ast_argument *list, const char *str, unsigned int source_line); diff --git a/src/lib-sieve/sieve-code.c b/src/lib-sieve/sieve-code.c index 7ce43a0583a2c385efd3644879cc0e15e7999318..254f2ea9dc4361f32d27b7b95cd224d4a3c35798 100644 --- a/src/lib-sieve/sieve-code.c +++ b/src/lib-sieve/sieve-code.c @@ -415,7 +415,7 @@ static bool opr_number_dump sieve_size_t number = 0; if (sieve_binary_read_integer(denv->sbin, address, &number) ) { - sieve_code_dumpf(denv, "NUM: %ld", (long) number); + sieve_code_dumpf(denv, "NUM: %llu", (long long) number); return TRUE; } diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h index a0ae3f5476d9be51568c25714ee902e36b57e5dc..55af27a8a9e721c4cf630b0600559367da72178c 100644 --- a/src/lib-sieve/sieve-common.h +++ b/src/lib-sieve/sieve-common.h @@ -9,8 +9,12 @@ /* * Types */ - -typedef uint64_t sieve_number_t; + +typedef size_t sieve_size_t; +typedef uint32_t sieve_offset_t; +typedef uint32_t sieve_number_t; + +#define SIEVE_MAX_NUMBER ((sieve_number_t) -1) /* * Predeclarations @@ -71,7 +75,6 @@ struct sieve_operation; struct sieve_coded_stringlist; /* sieve-binary.h */ -typedef size_t sieve_size_t; struct sieve_binary; /* sieve-objects.h */ diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c index 4fa900b16fd49416257605b25ad60e75875cb3d6..b7be61e30404944948a5b4b84418b346663e4015 100644 --- a/src/lib-sieve/sieve-lexer.c +++ b/src/lib-sieve/sieve-lexer.c @@ -7,6 +7,7 @@ #include "istream.h" #include "sieve-common.h" +#include "sieve-limits.h" #include "sieve-error.h" #include "sieve-script.h" @@ -290,7 +291,7 @@ static inline int sieve_lexer_curchar(struct sieve_lexer *lexer) */ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) { - int start_line; + sieve_number_t start_line; string_t *str; /* Read first character */ @@ -405,12 +406,24 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) if ( sieve_lexer_curchar(lexer) == '\\' ) { sieve_lexer_shift(lexer); } - - str_append_c(str, sieve_lexer_curchar(lexer)); + + if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) + str_append_c(str, sieve_lexer_curchar(lexer)); + sieve_lexer_shift(lexer); } - + sieve_lexer_shift(lexer); + + if ( str_len(str) > SIEVE_MAX_STRING_LEN ) { + sieve_lexer_error(lexer, + "quoted string started at line %d is too long " + "(longer than %llu bytes)", start_line, + (long long) SIEVE_MAX_STRING_LEN); + lexer->token_type = STT_ERROR; + return FALSE; + } + lexer->token_type = STT_STRING; return TRUE; @@ -456,34 +469,61 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) default: /* number */ if ( IS_DIGIT(sieve_lexer_curchar(lexer)) ) { - int value = DIGIT_VAL(sieve_lexer_curchar(lexer)); + sieve_number_t value = DIGIT_VAL(sieve_lexer_curchar(lexer)); + bool overflow = FALSE; + sieve_lexer_shift(lexer); while ( IS_DIGIT(sieve_lexer_curchar(lexer)) ) { - value = value * 10 + DIGIT_VAL(sieve_lexer_curchar(lexer)); + sieve_number_t valnew = + value * 10 + DIGIT_VAL(sieve_lexer_curchar(lexer)); + + /* Check for integer wrap */ + if ( valnew < value ) + overflow = TRUE; + + value = valnew; sieve_lexer_shift(lexer); } switch ( sieve_lexer_curchar(lexer) ) { case 'k': case 'K': /* Kilo */ - value *= 1024; + if ( value > (SIEVE_MAX_NUMBER >> 10) ) + overflow = TRUE; + else + value = value << 10; sieve_lexer_shift(lexer); break; case 'm': case 'M': /* Mega */ - value *= 1024*1024; + if ( value > (SIEVE_MAX_NUMBER >> 20) ) + overflow = TRUE; + else + value = value << 20; sieve_lexer_shift(lexer); break; case 'g': case 'G': /* Giga */ - value *= 1024*1024*1024; + if ( value > (SIEVE_MAX_NUMBER >> 30) ) + overflow = TRUE; + else + value = value << 30; sieve_lexer_shift(lexer); break; default: /* Next token */ break; } + + /* Check for integer wrap */ + if ( overflow ) { + sieve_lexer_error(lexer, + "number exceeds integer limits (max %llu)", + (long long) SIEVE_MAX_NUMBER); + lexer->token_type = STT_ERROR; + return FALSE; + } lexer->token_type = STT_NUMBER; lexer->token_int_value = value; @@ -531,6 +571,8 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) type == STT_IDENTIFIER && str_len(str) == 4 && strncasecmp(str_c(str), "text", 4) == 0 ) { sieve_lexer_shift(lexer); // discard colon + + start_line = lexer->current_line; /* Discard SP and HTAB whitespace */ while ( sieve_lexer_curchar(lexer) == ' ' || @@ -581,6 +623,16 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) if ( sieve_lexer_curchar(lexer) == '\n' ) { sieve_lexer_shift(lexer); + + if ( str_len(str) > SIEVE_MAX_STRING_LEN ) { + sieve_lexer_error(lexer, + "literal string started at line %d is too long " + "(longer than %llu bytes)", start_line, + (long long) SIEVE_MAX_STRING_LEN); + lexer->token_type = STT_ERROR; + return FALSE; + } + lexer->token_type = STT_STRING; return TRUE; } else if ( cr_shifted ) { @@ -591,7 +643,8 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) } /* Handle dot-stuffing */ - str_append_c(str, '.'); + if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) + str_append_c(str, '.'); if ( sieve_lexer_curchar(lexer) == '.' ) sieve_lexer_shift(lexer); } @@ -604,11 +657,16 @@ static bool sieve_lexer_scan_raw_token(struct sieve_lexer *lexer) lexer->token_type = STT_ERROR; return FALSE; } - - str_append_c(str, sieve_lexer_curchar(lexer)); + + if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) + str_append_c(str, sieve_lexer_curchar(lexer)); + sieve_lexer_shift(lexer); } - str_append_c(str, '\n'); + + if ( str_len(str) <= SIEVE_MAX_STRING_LEN ) + str_append_c(str, '\n'); + sieve_lexer_shift(lexer); } diff --git a/src/lib-sieve/sieve-limits.h b/src/lib-sieve/sieve-limits.h new file mode 100644 index 0000000000000000000000000000000000000000..fea09d8d7b433b7de22210022e8f6d9d191030be --- /dev/null +++ b/src/lib-sieve/sieve-limits.h @@ -0,0 +1,24 @@ +#ifndef __SIEVE_LIMITS_H +#define __SIEVE_LIMITS_H + +/* + * Lexer + */ + +#define SIEVE_MAX_STRING_LEN (1 << 20) + +/* + * AST + */ + +#define SIEVE_MAX_BLOCK_NESTING 32 +#define SIEVE_MAX_TEST_NESTING 32 + +/* + * Actions + */ + +#define SIEVE_DEFAULT_MAX_REDIRECTS 4 +#define SIEVE_DEFAULT_MAX_ACTIONS 32 + +#endif diff --git a/src/lib-sieve/sieve-parser.c b/src/lib-sieve/sieve-parser.c index a33d89d8d0e3f975238a07e6e86efc2be4aa0e4d..84306fa737ee53d07874e6a126d9dcce717099a0 100644 --- a/src/lib-sieve/sieve-parser.c +++ b/src/lib-sieve/sieve-parser.c @@ -26,7 +26,7 @@ inline static void sieve_parser_error inline static void sieve_parser_warning (struct sieve_parser *parser, const char *fmt, ...) ATTR_FORMAT(2, 3); -static bool sieve_parser_recover +static int sieve_parser_recover (struct sieve_parser *parser, enum sieve_token_type end_token); /* @@ -148,17 +148,18 @@ inline static void sieve_parser_warning * test-list = "(" test *("," test) ")" * test = identifier arguments */ -static bool sieve_parse_arguments +static int sieve_parse_arguments (struct sieve_parser *parser, struct sieve_ast_node *node) { struct sieve_lexer *lexer = parser->lexer; struct sieve_ast_node *test = NULL; - bool argument = TRUE; - bool result = TRUE; /* Indicates whether the parser is in a defined, not + bool test_present = TRUE; + bool arg_present = TRUE; + int result = TRUE; /* Indicates whether the parser is in a defined, not necessarily error-free state */ /* Parse arguments */ - while ( argument && result && + while ( arg_present && result > 0 && (parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) { struct sieve_ast_argument *arg; @@ -169,27 +170,33 @@ static bool sieve_parse_arguments /* Create stinglist object */ arg = sieve_ast_argument_stringlist_create (node, sieve_lexer_current_line(parser->lexer)); + + if ( arg == NULL ) break; sieve_lexer_skip_token(lexer); if ( sieve_lexer_current_token(lexer) == STT_STRING ) { + bool add_failed = FALSE; + /* Add the string to the list */ - sieve_ast_stringlist_add + if ( !sieve_ast_stringlist_add (arg, sieve_lexer_token_str(lexer), - sieve_lexer_current_line(parser->lexer)); + sieve_lexer_current_line(parser->lexer)) ) + add_failed = TRUE; sieve_lexer_skip_token(lexer); - while ( sieve_lexer_current_token(lexer) == STT_COMMA && + while ( !add_failed && sieve_lexer_current_token(lexer) == STT_COMMA && (parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) { sieve_lexer_skip_token(lexer); if ( sieve_lexer_current_token(lexer) == STT_STRING ) { /* Add the string to the list */ - sieve_ast_stringlist_add + if ( !sieve_ast_stringlist_add (arg, sieve_lexer_token_str(lexer), - sieve_lexer_current_line(parser->lexer)); + sieve_lexer_current_line(parser->lexer)) ) + add_failed = TRUE; sieve_lexer_skip_token(lexer); } else { @@ -201,6 +208,12 @@ static bool sieve_parse_arguments break; } } + + if ( add_failed ) { + sieve_parser_error(parser, + "failed to accept more items in string list"); + return -1; + } } else { sieve_parser_error(parser, "expecting string after '[' in string list, but found %s", @@ -225,15 +238,16 @@ static bool sieve_parse_arguments /* Single string */ case STT_STRING: - (void) sieve_ast_argument_string_create + arg = sieve_ast_argument_string_create (node, sieve_lexer_token_str(lexer), sieve_lexer_current_line(parser->lexer)); + sieve_lexer_skip_token(lexer); break; /* Number */ case STT_NUMBER: - (void) sieve_ast_argument_number_create + arg = sieve_ast_argument_number_create (node, sieve_lexer_token_int(lexer), sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); @@ -241,7 +255,7 @@ static bool sieve_parse_arguments /* Tag */ case STT_TAG: - (void) sieve_ast_argument_tag_create + arg = sieve_ast_argument_tag_create (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); @@ -249,9 +263,15 @@ static bool sieve_parse_arguments /* End of argument list, continue with tests */ default: - argument = FALSE; + arg_present = FALSE; break; } + + if ( arg_present && arg == NULL ) { + sieve_parser_error(parser, + "failed to accept more arguments for command '%s'", node->identifier); + return -1; + } } if ( !result ) return FALSE; /* Defer recovery to caller */ @@ -269,6 +289,9 @@ static bool sieve_parse_arguments sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); + /* Theoretically, test can be NULL */ + if ( test == NULL ) break; + /* Parse test arguments, which may include more tests (recurse) */ if ( !sieve_parse_arguments(parser, test) ) { return FALSE; /* Defer recovery to caller */ @@ -289,8 +312,10 @@ static bool sieve_parse_arguments sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); + if ( test == NULL ) break; + /* Parse test arguments, which may include more tests (recurse) */ - if ( sieve_parse_arguments(parser, test) ) { + if ( (result=sieve_parse_arguments(parser, test)) > 0 ) { /* More tests ? */ while ( sieve_lexer_current_token(lexer) == STT_COMMA && @@ -303,9 +328,13 @@ static bool sieve_parse_arguments (node, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); sieve_lexer_skip_token(lexer); + + if ( test == NULL ) break; /* Parse test arguments, which may include more tests (recurse) */ - if ( !sieve_parse_arguments(parser, test) ) { + if ( (result=sieve_parse_arguments(parser, test)) <= 0 ) { + if ( result < 0 ) return result; + result = sieve_parser_recover(parser, STT_RBRACKET); break; } @@ -318,8 +347,13 @@ static bool sieve_parse_arguments break; } } - } else + + if ( test == NULL ) break; + } else { + if ( result < 0 ) return result; + result = sieve_parser_recover(parser, STT_RBRACKET); + } } else { sieve_parser_error(parser, "expecting test identifier after '(' in test list, but found %s", @@ -342,7 +376,7 @@ static bool sieve_parse_arguments /* Recover function tries to make next token equal to ')'. If it succeeds * we need to skip it. */ - if ( (result = sieve_parser_recover(parser, STT_RBRACKET)) == TRUE ) + if ( (result=sieve_parser_recover(parser, STT_RBRACKET)) == TRUE ) sieve_lexer_skip_token(lexer); } break; @@ -351,8 +385,15 @@ static bool sieve_parse_arguments /* Not an error: test / test-list is optional * --> any errors are detected by the caller */ + test_present = FALSE; break; - } + } + + if ( test_present && test == NULL ) { + sieve_parser_error(parser, + "failed to accept more tests for command '%s'", node->identifier); + return -1; + } return result; } @@ -361,11 +402,11 @@ static bool sieve_parse_arguments * command = identifier arguments ( ";" / block ) * block = "{" commands "}" */ -static bool sieve_parse_commands +static int sieve_parse_commands (struct sieve_parser *parser, struct sieve_ast_node *block) { struct sieve_lexer *lexer = parser->lexer; - bool result = TRUE; + int result = TRUE; while ( sieve_lexer_current_token(lexer) == STT_IDENTIFIER && (parser->valid || sieve_errors_more_allowed(parser->ehandler)) ) { @@ -374,6 +415,13 @@ static bool sieve_parse_commands (block, sieve_lexer_token_ident(lexer), sieve_lexer_current_line(parser->lexer)); + if ( command == NULL ) { + sieve_parser_error(parser, + "failed to accept more commands inside block of command '%s'", + block->identifier); + return -1; + } + /* Defined state */ result = TRUE; @@ -384,7 +432,7 @@ static bool sieve_parse_commands /* Check whether the command is properly terminated * (i.e. with ; or a new block) */ - if ( result && + if ( result > 0 && sieve_lexer_current_token(lexer) != STT_SEMICOLON && sieve_lexer_current_token(lexer) != STT_LCURLY ) { @@ -396,12 +444,12 @@ static bool sieve_parse_commands } /* Try to recover from parse errors to reacquire a defined state */ - if ( !result ) { + if ( result == 0 ) { result = sieve_parser_recover(parser, STT_SEMICOLON); } /* Don't bother to continue if we are not in a defined state */ - if ( !result ) return FALSE; + if ( result <= 0 ) return result; switch ( sieve_lexer_current_token(lexer) ) { @@ -415,7 +463,7 @@ static bool sieve_parse_commands command->block = TRUE; - if ( sieve_parse_commands(parser, command) ) { + if ( (result=sieve_parse_commands(parser, command)) > 0 ) { if ( sieve_lexer_current_token(lexer) != STT_RCURLY ) { sieve_parser_error(parser, @@ -424,8 +472,12 @@ static bool sieve_parse_commands result = sieve_parser_recover(parser, STT_RCURLY); } else sieve_lexer_skip_token(lexer); - } else if ( (result = sieve_parser_recover(parser, STT_RCURLY)) == TRUE ) - sieve_lexer_skip_token(lexer); + } else { + if ( result < 0 ) return result; + + if ( (result=sieve_parser_recover(parser, STT_RCURLY)) == 0 ) + sieve_lexer_skip_token(lexer); + } break; @@ -456,7 +508,7 @@ bool sieve_parser_run sieve_lexer_skip_token(parser->lexer); /* Parse */ - if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) && + if ( sieve_parse_commands(parser, sieve_ast_root(parser->ast)) > 0 && parser->valid ) { /* Parsed right to EOF ? */ @@ -516,7 +568,7 @@ static inline enum sieve_grammatical_prio __get_token_priority return SGP_OTHER; } -static bool sieve_parser_recover +static int sieve_parser_recover (struct sieve_parser *parser, enum sieve_token_type end_token) { /* The tokens that begin/end a specific block/command/list in order diff --git a/tests/compile/errors.svtest b/tests/compile/errors.svtest index 028ce6b4025de849d253bc6e27eae797424dab15..d19de2d0ce1dcc1d474b06dfd7c9d719d937c70e 100644 --- a/tests/compile/errors.svtest +++ b/tests/compile/errors.svtest @@ -8,6 +8,20 @@ require "comparator-i;ascii-numeric"; * tested commands, but we want to be thorough. */ +/* + * Lexer errors + */ + +test "Lexer errors (FIXME: count only)" { + if test_compile "errors/lexer.sieve" { + test_fail "compile should have failed."; + } + + if not test_error :count "eq" :comparator "i;ascii-numeric" "8" { + test_fail "wrong number of errors reported"; + } +} + /* * Header test */ diff --git a/tests/compile/errors/lexer.sieve b/tests/compile/errors/lexer.sieve new file mode 100644 index 0000000000000000000000000000000000000000..200e2a44fd163b1f48fc975392b24cbe4197e4f9 --- /dev/null +++ b/tests/compile/errors/lexer.sieve @@ -0,0 +1,54 @@ +/* + * Lexer tests + * + * Total errors: 7 (+1 = 8) + */ +# Number too large +if size :under 4294967300 { + stop; +} + +# Number too large +if size :under 4294967296 { + stop; +} + +# Number too large +if size :over 35651584k { + stop; +} + +# Number too large +if size :over 34816M { + stop; +} + +# Number too large +if size :over 34G { + stop; +} + +# Number too large +if size :over 4G { + stop; +} + +# Number far too large +if size :over 49834598293485814273947921734981723971293741923 { + stop; +} + +# Not an error +if size :under 4294967295 { + stop; +} + +# Not an error +if size :under 4294967294 { + stop; +} + +# Not an error +if size :under 1G { + stop; +}