diff --git a/libyara/hex_lexer.c b/libyara/hex_lexer.c index 97c80f05dd..209bab0df8 100644 --- a/libyara/hex_lexer.c +++ b/libyara/hex_lexer.c @@ -735,6 +735,7 @@ with noyywrap then we can remove this pragma. #pragma GCC diagnostic ignored "-Wunused-function" #endif +#include #include #include @@ -750,6 +751,7 @@ with noyywrap then we can remove this pragma. #ifdef _WIN32 #define snprintf _snprintf +#define strtoll _strtoi64 #endif #define ERROR_IF(x, error) \ @@ -760,11 +762,11 @@ with noyywrap then we can remove this pragma. YYABORT; \ } \ -#line 763 "libyara/hex_lexer.c" +#line 765 "libyara/hex_lexer.c" #define YY_NO_UNISTD_H 1 #define YY_NO_INPUT 1 -#line 767 "libyara/hex_lexer.c" +#line 769 "libyara/hex_lexer.c" #define INITIAL 0 #define comment 1 @@ -1038,11 +1040,11 @@ YY_DECL } { -#line 95 "libyara/hex_lexer.l" +#line 97 "libyara/hex_lexer.l" -#line 1045 "libyara/hex_lexer.c" +#line 1047 "libyara/hex_lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { @@ -1109,7 +1111,7 @@ YY_DECL case 1: YY_RULE_SETUP -#line 98 "libyara/hex_lexer.l" +#line 100 "libyara/hex_lexer.l" { yylval->integer = xtoi(yytext); @@ -1118,7 +1120,7 @@ YY_RULE_SETUP YY_BREAK case 2: YY_RULE_SETUP -#line 104 "libyara/hex_lexer.l" +#line 106 "libyara/hex_lexer.l" { yytext[1] = '0'; // replace ? by 0 @@ -1128,7 +1130,7 @@ YY_RULE_SETUP YY_BREAK case 3: YY_RULE_SETUP -#line 111 "libyara/hex_lexer.l" +#line 113 "libyara/hex_lexer.l" { yylval->integer = xtoi(&(yytext[1])); @@ -1137,7 +1139,7 @@ YY_RULE_SETUP YY_BREAK case 4: YY_RULE_SETUP -#line 117 "libyara/hex_lexer.l" +#line 119 "libyara/hex_lexer.l" { yytext[0] = '0'; // replace ? by 0 @@ -1147,7 +1149,7 @@ YY_RULE_SETUP YY_BREAK case 5: YY_RULE_SETUP -#line 124 "libyara/hex_lexer.l" +#line 126 "libyara/hex_lexer.l" { yylval->integer = 0x0000; @@ -1156,7 +1158,7 @@ YY_RULE_SETUP YY_BREAK case 6: YY_RULE_SETUP -#line 130 "libyara/hex_lexer.l" +#line 132 "libyara/hex_lexer.l" { yytext[2] = '0'; // replace ? by 0 @@ -1166,7 +1168,7 @@ YY_RULE_SETUP YY_BREAK case 7: YY_RULE_SETUP -#line 137 "libyara/hex_lexer.l" +#line 139 "libyara/hex_lexer.l" { yytext[1] = '0'; // replace ? by 0 @@ -1176,7 +1178,7 @@ YY_RULE_SETUP YY_BREAK case 8: YY_RULE_SETUP -#line 144 "libyara/hex_lexer.l" +#line 146 "libyara/hex_lexer.l" { yyerror(yyscanner, lex_env, "uneven number of digits in hex string"); @@ -1185,7 +1187,7 @@ YY_RULE_SETUP YY_BREAK case 9: YY_RULE_SETUP -#line 150 "libyara/hex_lexer.l" +#line 152 "libyara/hex_lexer.l" { yyerror(yyscanner, lex_env, "invalid not operator (~) in hex string"); @@ -1194,7 +1196,7 @@ YY_RULE_SETUP YY_BREAK case 10: YY_RULE_SETUP -#line 157 "libyara/hex_lexer.l" +#line 159 "libyara/hex_lexer.l" { BEGIN(range); @@ -1203,7 +1205,7 @@ YY_RULE_SETUP YY_BREAK case 11: YY_RULE_SETUP -#line 163 "libyara/hex_lexer.l" +#line 165 "libyara/hex_lexer.l" { BEGIN(comment); @@ -1211,7 +1213,7 @@ YY_RULE_SETUP YY_BREAK case 12: YY_RULE_SETUP -#line 168 "libyara/hex_lexer.l" +#line 170 "libyara/hex_lexer.l" { BEGIN(INITIAL); @@ -1220,17 +1222,17 @@ YY_RULE_SETUP case 13: /* rule 13 can match eol */ YY_RULE_SETUP -#line 173 "libyara/hex_lexer.l" +#line 175 "libyara/hex_lexer.l" // skip comments YY_BREAK case 14: YY_RULE_SETUP -#line 175 "libyara/hex_lexer.l" +#line 177 "libyara/hex_lexer.l" // skip single-line comments YY_BREAK case 15: YY_RULE_SETUP -#line 177 "libyara/hex_lexer.l" +#line 179 "libyara/hex_lexer.l" { return yytext[0]; @@ -1238,16 +1240,30 @@ YY_RULE_SETUP YY_BREAK case 16: YY_RULE_SETUP -#line 182 "libyara/hex_lexer.l" +#line 184 "libyara/hex_lexer.l" { - yylval->integer = atoi(yytext); + // strtoll is used instead of atoi because atoi has undefined behaviour for + // numbers that don't fit in an int. Numbers too large for a long long are + // clamped to LLONG_MAX by strtoll, which is still caught by the check below. + + yylval->integer = strtoll(yytext, NULL, 10); + + // Jump lengths end up stored in the int fields of a RE_NODE, anything above + // INT_MAX can't be represented. + + if (yylval->integer > INT_MAX) + { + yyerror(yyscanner, lex_env, "jump length too large"); + yyterminate(); + } + return _NUMBER_; } YY_BREAK case 17: YY_RULE_SETUP -#line 188 "libyara/hex_lexer.l" +#line 204 "libyara/hex_lexer.l" { BEGIN(INITIAL); @@ -1257,12 +1273,12 @@ YY_RULE_SETUP case 18: /* rule 18 can match eol */ YY_RULE_SETUP -#line 194 "libyara/hex_lexer.l" +#line 210 "libyara/hex_lexer.l" // skip whitespaces YY_BREAK case 19: YY_RULE_SETUP -#line 196 "libyara/hex_lexer.l" +#line 212 "libyara/hex_lexer.l" { yyerror(yyscanner, lex_env, "invalid character in hex string jump"); @@ -1272,12 +1288,12 @@ YY_RULE_SETUP case 20: /* rule 20 can match eol */ YY_RULE_SETUP -#line 202 "libyara/hex_lexer.l" +#line 218 "libyara/hex_lexer.l" // skip whitespaces YY_BREAK case 21: YY_RULE_SETUP -#line 204 "libyara/hex_lexer.l" +#line 220 "libyara/hex_lexer.l" { // pass valid characters to the parser return yytext[0]; @@ -1285,7 +1301,7 @@ YY_RULE_SETUP YY_BREAK case 22: YY_RULE_SETUP -#line 209 "libyara/hex_lexer.l" +#line 225 "libyara/hex_lexer.l" { // reject all other characters yyerror(yyscanner, lex_env, "invalid character in hex string"); @@ -1294,10 +1310,10 @@ YY_RULE_SETUP YY_BREAK case 23: YY_RULE_SETUP -#line 215 "libyara/hex_lexer.l" +#line 231 "libyara/hex_lexer.l" ECHO; YY_BREAK -#line 1300 "libyara/hex_lexer.c" +#line 1316 "libyara/hex_lexer.c" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(comment): case YY_STATE_EOF(range): @@ -2450,7 +2466,7 @@ void yyfree (void * ptr , yyscan_t yyscanner) #define YYTABLES_NAME "yytables" -#line 215 "libyara/hex_lexer.l" +#line 231 "libyara/hex_lexer.l" // diff --git a/libyara/hex_lexer.l b/libyara/hex_lexer.l index ad99244905..887dd98c42 100644 --- a/libyara/hex_lexer.l +++ b/libyara/hex_lexer.l @@ -44,6 +44,7 @@ with noyywrap then we can remove this pragma. #pragma GCC diagnostic ignored "-Wunused-function" #endif +#include #include #include @@ -59,6 +60,7 @@ with noyywrap then we can remove this pragma. #ifdef _WIN32 #define snprintf _snprintf +#define strtoll _strtoi64 #endif #define ERROR_IF(x, error) \ @@ -181,7 +183,21 @@ hexdigit [a-fA-F0-9] {digit}+ { - yylval->integer = atoi(yytext); + // strtoll is used instead of atoi because atoi has undefined behaviour for + // numbers that don't fit in an int. Numbers too large for a long long are + // clamped to LLONG_MAX by strtoll, which is still caught by the check below. + + yylval->integer = strtoll(yytext, NULL, 10); + + // Jump lengths end up stored in the int fields of a RE_NODE, anything above + // INT_MAX can't be represented. + + if (yylval->integer > INT_MAX) + { + yyerror(yyscanner, lex_env, "jump length too large"); + yyterminate(); + } + return _NUMBER_; } diff --git a/libyara/re_lexer.c b/libyara/re_lexer.c index 34b948cdf9..78c15485c2 100644 --- a/libyara/re_lexer.c +++ b/libyara/re_lexer.c @@ -770,6 +770,7 @@ with noyywrap then we can remove this pragma. #ifdef _WIN32 #define snprintf _snprintf +#define strtoll _strtoi64 #endif // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit @@ -800,10 +801,10 @@ int read_escaped_char( uint8_t* escaped_char, bool strict_escape); -#line 803 "libyara/re_lexer.c" +#line 804 "libyara/re_lexer.c" #define YY_NO_UNISTD_H 1 -#line 806 "libyara/re_lexer.c" +#line 807 "libyara/re_lexer.c" #define INITIAL 0 #define char_class 1 @@ -1076,10 +1077,10 @@ YY_DECL } { -#line 114 "libyara/re_lexer.l" +#line 115 "libyara/re_lexer.l" -#line 1082 "libyara/re_lexer.c" +#line 1083 "libyara/re_lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { @@ -1146,13 +1147,18 @@ YY_DECL case 1: YY_RULE_SETUP -#line 116 "libyara/re_lexer.l" +#line 117 "libyara/re_lexer.l" { // Examples: {3,8} {3, 8}, {3 ,8} {3 , 8} {0,5} {,5} {7,} - int hi_bound; - int lo_bound = atoi(yytext + 1); + // strtoll is used instead of atoi because atoi has undefined behaviour for + // numbers that don't fit in an int. Numbers too large for a long long are + // clamped to LLONG_MAX by strtoll, which is still caught by the RE_MAX_RANGE + // check below. + + long long hi_bound; + long long lo_bound = strtoll(yytext + 1, NULL, 10); char* comma = strchr(yytext, ','); char* hi_bound_ptr = comma + 1; @@ -1163,7 +1169,7 @@ YY_RULE_SETUP if (*hi_bound_ptr == '}') hi_bound = RE_MAX_RANGE; else - hi_bound = atoi(hi_bound_ptr); + hi_bound = strtoll(hi_bound_ptr, NULL, 10); if (hi_bound > RE_MAX_RANGE) { @@ -1171,43 +1177,40 @@ YY_RULE_SETUP yyterminate(); } - if (hi_bound < lo_bound || hi_bound < 0 || lo_bound < 0) + if (hi_bound < lo_bound) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } - yylval->range = (hi_bound << 16) | lo_bound; + yylval->range = (uint32_t) ((hi_bound << 16) | lo_bound); return _RANGE_; } YY_BREAK case 2: YY_RULE_SETUP -#line 152 "libyara/re_lexer.l" +#line 158 "libyara/re_lexer.l" { // Example: {10} - int value = atoi(yytext + 1); - - // atoi can return a negative value if the input string represents a number - // too large to fit in an integer. + long long value = strtoll(yytext + 1, NULL, 10); - if (value > RE_MAX_RANGE || value < 0) + if (value > RE_MAX_RANGE) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } - yylval->range = (value << 16) | value; + yylval->range = (uint32_t) ((value << 16) | value); return _RANGE_; } YY_BREAK case 3: YY_RULE_SETUP -#line 173 "libyara/re_lexer.l" +#line 176 "libyara/re_lexer.l" { // Start of a negated character class. Example: [^abcd] @@ -1219,7 +1222,7 @@ YY_RULE_SETUP YY_BREAK case 4: YY_RULE_SETUP -#line 182 "libyara/re_lexer.l" +#line 185 "libyara/re_lexer.l" { // Start of character negated class containing a ]. @@ -1234,7 +1237,7 @@ YY_RULE_SETUP YY_BREAK case 5: YY_RULE_SETUP -#line 195 "libyara/re_lexer.l" +#line 198 "libyara/re_lexer.l" { // Start of character class containing a ]. @@ -1249,7 +1252,7 @@ YY_RULE_SETUP YY_BREAK case 6: YY_RULE_SETUP -#line 208 "libyara/re_lexer.l" +#line 211 "libyara/re_lexer.l" { // Start of character class. Example: [abcd] @@ -1262,7 +1265,7 @@ YY_RULE_SETUP case 7: /* rule 7 can match eol */ YY_RULE_SETUP -#line 218 "libyara/re_lexer.l" +#line 221 "libyara/re_lexer.l" { // Any non-special character is passed as a CHAR token to the scanner. @@ -1273,63 +1276,63 @@ YY_RULE_SETUP YY_BREAK case 8: YY_RULE_SETUP -#line 227 "libyara/re_lexer.l" +#line 230 "libyara/re_lexer.l" { return _WORD_CHAR_; } YY_BREAK case 9: YY_RULE_SETUP -#line 232 "libyara/re_lexer.l" +#line 235 "libyara/re_lexer.l" { return _NON_WORD_CHAR_; } YY_BREAK case 10: YY_RULE_SETUP -#line 237 "libyara/re_lexer.l" +#line 240 "libyara/re_lexer.l" { return _SPACE_; } YY_BREAK case 11: YY_RULE_SETUP -#line 242 "libyara/re_lexer.l" +#line 245 "libyara/re_lexer.l" { return _NON_SPACE_; } YY_BREAK case 12: YY_RULE_SETUP -#line 247 "libyara/re_lexer.l" +#line 250 "libyara/re_lexer.l" { return _DIGIT_; } YY_BREAK case 13: YY_RULE_SETUP -#line 252 "libyara/re_lexer.l" +#line 255 "libyara/re_lexer.l" { return _NON_DIGIT_; } YY_BREAK case 14: YY_RULE_SETUP -#line 257 "libyara/re_lexer.l" +#line 260 "libyara/re_lexer.l" { return _WORD_BOUNDARY_; } YY_BREAK case 15: YY_RULE_SETUP -#line 261 "libyara/re_lexer.l" +#line 264 "libyara/re_lexer.l" { return _NON_WORD_BOUNDARY_; } YY_BREAK case 16: YY_RULE_SETUP -#line 266 "libyara/re_lexer.l" +#line 269 "libyara/re_lexer.l" { yyerror(yyscanner, lex_env, "backreferences are not allowed"); @@ -1338,7 +1341,7 @@ YY_RULE_SETUP YY_BREAK case 17: YY_RULE_SETUP -#line 273 "libyara/re_lexer.l" +#line 276 "libyara/re_lexer.l" { uint8_t c; @@ -1365,7 +1368,7 @@ YY_RULE_SETUP YY_BREAK case 18: YY_RULE_SETUP -#line 298 "libyara/re_lexer.l" +#line 301 "libyara/re_lexer.l" { // End of character class. @@ -1381,7 +1384,7 @@ YY_RULE_SETUP case 19: /* rule 19 can match eol */ YY_RULE_SETUP -#line 312 "libyara/re_lexer.l" +#line 315 "libyara/re_lexer.l" { // A range inside a character class. The regexp is... @@ -1446,7 +1449,7 @@ YY_RULE_SETUP YY_BREAK case 20: YY_RULE_SETUP -#line 375 "libyara/re_lexer.l" +#line 378 "libyara/re_lexer.l" { for (int i = 0; i < 32; i++) @@ -1455,7 +1458,7 @@ YY_RULE_SETUP YY_BREAK case 21: YY_RULE_SETUP -#line 382 "libyara/re_lexer.l" +#line 385 "libyara/re_lexer.l" { for (int i = 0; i < 32; i++) @@ -1464,7 +1467,7 @@ YY_RULE_SETUP YY_BREAK case 22: YY_RULE_SETUP -#line 389 "libyara/re_lexer.l" +#line 392 "libyara/re_lexer.l" { for (int i = 0; i < 32; i++) @@ -1473,7 +1476,7 @@ YY_RULE_SETUP YY_BREAK case 23: YY_RULE_SETUP -#line 396 "libyara/re_lexer.l" +#line 399 "libyara/re_lexer.l" { for (int i = 0; i < 32; i++) @@ -1482,7 +1485,7 @@ YY_RULE_SETUP YY_BREAK case 24: YY_RULE_SETUP -#line 403 "libyara/re_lexer.l" +#line 406 "libyara/re_lexer.l" { for (char c = '0'; c <= '9'; c++) @@ -1491,7 +1494,7 @@ YY_RULE_SETUP YY_BREAK case 25: YY_RULE_SETUP -#line 410 "libyara/re_lexer.l" +#line 413 "libyara/re_lexer.l" { for (int i = 0; i < 32; i++) @@ -1511,7 +1514,7 @@ YY_RULE_SETUP YY_BREAK case 26: YY_RULE_SETUP -#line 428 "libyara/re_lexer.l" +#line 431 "libyara/re_lexer.l" { uint8_t c; @@ -1536,7 +1539,7 @@ YY_RULE_SETUP YY_BREAK case 27: YY_RULE_SETUP -#line 451 "libyara/re_lexer.l" +#line 454 "libyara/re_lexer.l" { if (yytext[0] >= 32 && yytext[0] < 127) @@ -1554,7 +1557,7 @@ YY_RULE_SETUP } YY_BREAK case YY_STATE_EOF(char_class): -#line 468 "libyara/re_lexer.l" +#line 471 "libyara/re_lexer.l" { // End of regexp reached while scanning a character class. @@ -1565,7 +1568,7 @@ case YY_STATE_EOF(char_class): YY_BREAK case 28: YY_RULE_SETUP -#line 477 "libyara/re_lexer.l" +#line 480 "libyara/re_lexer.l" { if (yytext[0] >= 32 && yytext[0] < 127) @@ -1580,7 +1583,7 @@ YY_RULE_SETUP } YY_BREAK case YY_STATE_EOF(INITIAL): -#line 491 "libyara/re_lexer.l" +#line 494 "libyara/re_lexer.l" { yyterminate(); @@ -1588,10 +1591,10 @@ case YY_STATE_EOF(INITIAL): YY_BREAK case 29: YY_RULE_SETUP -#line 496 "libyara/re_lexer.l" +#line 499 "libyara/re_lexer.l" ECHO; YY_BREAK -#line 1594 "libyara/re_lexer.c" +#line 1597 "libyara/re_lexer.c" case YY_END_OF_BUFFER: { @@ -2740,7 +2743,7 @@ void yyfree (void * ptr , yyscan_t yyscanner) #define YYTABLES_NAME "yytables" -#line 496 "libyara/re_lexer.l" +#line 499 "libyara/re_lexer.l" int escaped_char_value( diff --git a/libyara/re_lexer.l b/libyara/re_lexer.l index affe9581d4..657c45dded 100644 --- a/libyara/re_lexer.l +++ b/libyara/re_lexer.l @@ -61,6 +61,7 @@ with noyywrap then we can remove this pragma. #ifdef _WIN32 #define snprintf _snprintf +#define strtoll _strtoi64 #endif // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit @@ -117,8 +118,13 @@ hex_digit [0-9a-fA-F] // Examples: {3,8} {3, 8}, {3 ,8} {3 , 8} {0,5} {,5} {7,} - int hi_bound; - int lo_bound = atoi(yytext + 1); + // strtoll is used instead of atoi because atoi has undefined behaviour for + // numbers that don't fit in an int. Numbers too large for a long long are + // clamped to LLONG_MAX by strtoll, which is still caught by the RE_MAX_RANGE + // check below. + + long long hi_bound; + long long lo_bound = strtoll(yytext + 1, NULL, 10); char* comma = strchr(yytext, ','); char* hi_bound_ptr = comma + 1; @@ -129,7 +135,7 @@ hex_digit [0-9a-fA-F] if (*hi_bound_ptr == '}') hi_bound = RE_MAX_RANGE; else - hi_bound = atoi(hi_bound_ptr); + hi_bound = strtoll(hi_bound_ptr, NULL, 10); if (hi_bound > RE_MAX_RANGE) { @@ -137,13 +143,13 @@ hex_digit [0-9a-fA-F] yyterminate(); } - if (hi_bound < lo_bound || hi_bound < 0 || lo_bound < 0) + if (hi_bound < lo_bound) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } - yylval->range = (hi_bound << 16) | lo_bound; + yylval->range = (uint32_t) ((hi_bound << 16) | lo_bound); return _RANGE_; } @@ -153,18 +159,15 @@ hex_digit [0-9a-fA-F] // Example: {10} - int value = atoi(yytext + 1); - - // atoi can return a negative value if the input string represents a number - // too large to fit in an integer. + long long value = strtoll(yytext + 1, NULL, 10); - if (value > RE_MAX_RANGE || value < 0) + if (value > RE_MAX_RANGE) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } - yylval->range = (value << 16) | value; + yylval->range = (uint32_t) ((value << 16) | value); return _RANGE_; } diff --git a/tests/test-rules.c b/tests/test-rules.c index 8b80f2f794..3d7158c423 100644 --- a/tests/test-rules.c +++ b/tests/test-rules.c @@ -1703,6 +1703,20 @@ static void test_hex_strings() condition: $a ", ERROR_INVALID_HEX_STRING); + // Jump lengths that don't fit in an int must be rejected. 4294967297 used to + // wrap around to 1, so this compiled as { 61 [0-1] 62 }. + assert_error( + "rule test { \ + strings: $a = { 61 [0-4294967297] 62 } \ + condition: $a }", + ERROR_INVALID_HEX_STRING); + + assert_error( + "rule test { \ + strings: $a = { 61 [0-2147483648] 62 } \ + condition: $a }", + ERROR_INVALID_HEX_STRING); + /* TODO: tests.py:551 ff. */ YR_DEBUG_FPRINTF(1, stderr, "} // %s()\n", __FUNCTION__); @@ -2767,6 +2781,12 @@ void test_re() // Test for integer overflow in repeat interval assert_regexp_syntax_error("a{2977952116}"); + // Values that wrap around to a small positive number when truncated to an + // int must be rejected too, not silently turned into a{0} and b{1,1}. + assert_regexp_syntax_error("a{4294967296}"); + assert_regexp_syntax_error("b{1,4294967297}"); + assert_regexp_syntax_error("c{99999999999999999999}"); + assert_error( "rule test { strings: $a = /a\\/ condition: $a }", ERROR_SYNTAX_ERROR);