From a5cac25efd6ee857bab0b933ca5f7bbe1ede5074 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Sun, 7 May 2023 16:53:39 -0700 Subject: compiler: parse port numbers in ranges using the standard number parser. Run them through the same code that's used for numbers in the lexical analyzer, for consistency. --- scanner.l | 169 ++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 38 deletions(-) (limited to 'scanner.l') diff --git a/scanner.l b/scanner.l index 85fe395a..c20637b2 100644 --- a/scanner.l +++ b/scanner.l @@ -32,6 +32,26 @@ #include "grammar.h" #include "diag-control.h" + +/* + * Convert string to 32-bit unsigned integer; the string starts at + * string and is string_len bytes long. + * + * On success, sets *val to the value and returns 1. + * On failure, sets the BPF error string and returns 0. + * + * Also used in gencode.c + */ +typedef enum { + STOULEN_OK, + STOULEN_NOT_HEX_NUMBER, + STOULEN_NOT_OCTAL_NUMBER, + STOULEN_NOT_DECIMAL_NUMBER, + STOULEN_ERROR +} stoulen_ret; + +stoulen_ret stoulen(const char *string, size_t stringlen, bpf_u_int32 *val, + compiler_state_t *cstate); } /* @@ -149,7 +169,7 @@ void pcap_set_column(int, yyscan_t); #include "os-proto.h" #endif -static int stou(char *, YYSTYPE *, compiler_state_t *); +static int stou(const char *, YYSTYPE *, compiler_state_t *); /* * Disable diagnostics in the code generated by Flex. @@ -490,27 +510,20 @@ tcp-cwr { yylval->h = 0x80; return NUM; } */ DIAG_ON_FLEX -/* - * Convert string to 32-bit unsigned integer. Just like atoi(), but checks for - * preceding 0x or 0 and uses hex or octal instead of decimal. - * - * On success, sets yylval->h to the value and returns NUM. - * On failure, sets the BPF error string and returns LEX_ERROR, to force - * the parse to stop. - */ -static int -stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) +stoulen_ret +stoulen(const char *string, size_t string_len, bpf_u_int32 *val, + compiler_state_t *cstate) { bpf_u_int32 n = 0; unsigned int digit; - char *s = yytext_arg; + const char *s = string; /* - * yytext_arg is guaranteed either to be a string of decimal digits + * string is guaranteed either to be a string of decimal digits * or 0[xX] followed by a string of hex digits. */ - if (*s == '0') { - if (s[1] == 'x' || s[1] == 'X') { + if (string_len >= 1 && *s == '0') { + if (string_len >= 2 && (s[1] == 'x' || s[1] == 'X')) { /* * Begins with 0x or 0X, so hex. * Guaranteed to be all hex digits following the @@ -518,13 +531,25 @@ stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) * A-F. */ s += 2; /* skip the prefix */ - while ((digit = *s++) != '\0') { + string_len -= 2; + while (string_len != 0) { + digit = *s++; + string_len--; if (digit >= '0' && digit <= '9') digit = digit - '0'; else if (digit >= 'a' && digit <= 'f') digit = digit - 'a' + 10; - else + else if (digit >= 'A' && digit <= 'F') digit = digit - 'A' + 10; + else { + /* + * Not a valid hex number. + * Don't treat this as an error, + * in case the caller wants to + * interpret it as something else. + */ + return STOULEN_NOT_HEX_NUMBER; + } /* * Check for overflow. @@ -536,10 +561,10 @@ stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) * add 4 more; that won't fit * in 32 bits. */ - bpf_set_error(yyextra_arg, - "number %s overflows 32 bits", - yytext_arg); - return LEX_ERROR; + bpf_set_error(cstate, + "number %.*s overflows 32 bits", + (int)string_len, string); + return STOULEN_ERROR; } n = (n << 4) + digit; } @@ -551,14 +576,20 @@ stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) * report an error. */ s += 1; - while ((digit = *s++) != '\0') { + string_len -= 1; + while (string_len != 0) { + digit = *s++; + string_len--; if (digit >= '0' && digit <= '7') digit = digit - '0'; else { - bpf_set_error(yyextra_arg, - "number %s contains non-octal digit", - yytext_arg); - return LEX_ERROR; + /* + * Not a valid octal number. + * Don't treat this as an error, + * in case the caller wants to + * interpret it as something else. + */ + return STOULEN_NOT_OCTAL_NUMBER; } if (n > 03777777777U) { /* @@ -567,10 +598,10 @@ stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) * 3 more; that won't fit in * 32 bits. */ - bpf_set_error(yyextra_arg, - "number %s overflows 32 bits", - yytext_arg); - return LEX_ERROR; + bpf_set_error(cstate, + "number %.*s overflows 32 bits", + (int)string_len, string); + return STOULEN_ERROR; } n = (n << 3) + digit; } @@ -579,21 +610,83 @@ stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) /* * Decimal. */ - while ((digit = *s++) != '\0') { - digit = digit - '0'; + while (string_len != 0) { + digit = *s++; + string_len--; + if (digit >= '0' && digit <= '9') + digit = digit - '0'; + else { + /* + * Not a valid decimal number. + * Don't treat this as an error, + * in case the caller wants to + * interpret it as something else. + */ + return STOULEN_NOT_DECIMAL_NUMBER; + } #define CUTOFF_DEC (0xFFFFFFFFU / 10U) #define CUTLIM_DEC (0xFFFFFFFFU % 10U) if (n > CUTOFF_DEC || (n == CUTOFF_DEC && digit > CUTLIM_DEC)) { - bpf_set_error(yyextra_arg, - "number %s overflows 32 bits", - yytext_arg); - return LEX_ERROR; + /* + * Adding that digit will result in a + * number that won't fit in 32 bits. + */ + bpf_set_error(cstate, + "number %.*s overflows 32 bits", + (int)string_len, string); + return STOULEN_ERROR; } n = (n * 10) + digit; } } - yylval_arg->h = n; - return NUM; + *val = n; + return STOULEN_OK; +} + +/* + * Convert string to 32-bit unsigned integer. Just like atoi(), but checks for + * preceding 0x or 0 and uses hex or octal instead of decimal. + * + * On success, sets yylval->h to the value and returns NUM. + * On failure, sets the BPF error string and returns LEX_ERROR, to force + * the parse to stop. + */ +static int +stou(const char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) +{ + stoulen_ret ret; + + ret = stoulen(yytext_arg, strlen(yytext_arg), &yylval_arg->h, + yyextra_arg); + switch (ret) { + + case STOULEN_OK: + return NUM; + + case STOULEN_NOT_OCTAL_NUMBER: + bpf_set_error(yyextra_arg, "number %s contains non-octal digit", + yytext_arg); + return LEX_ERROR; + + case STOULEN_NOT_HEX_NUMBER: + bpf_set_error(yyextra_arg, "number %s contains non-hex digit", + yytext_arg); + return LEX_ERROR; + + case STOULEN_NOT_DECIMAL_NUMBER: + bpf_set_error(yyextra_arg, "number %s contains non-decimal digit", + yytext_arg); + return LEX_ERROR; + + case STOULEN_ERROR: + /* Error already set. */ + return LEX_ERROR; + + default: + /* Should not happen */ + bpf_set_error(yyextra_arg, "stoulen returned %d", ret); + return LEX_ERROR; + } } -- cgit v1.2.1