From 36a8e8afba5ac858d509f6d3a5b7ccaaf1c21aea Mon Sep 17 00:00:00 2001 From: Reijo Tomperi Date: Sun, 16 Aug 2009 23:28:17 +0300 Subject: [PATCH] Fix ticket #543 (Possible buffer overrun in Token::Match()) http://sourceforge.net/apps/trac/cppcheck/ticket/543 Get rid of string copying in Token::Match(), also improves speed a little. 8.0s -> 7.7s --- src/token.cpp | 133 ++++++++++++++++++++++++++++++++++++++------------ src/token.h | 22 +++++++++ 2 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/token.cpp b/src/token.cpp index 480781d56..8d66608f3 100644 --- a/src/token.cpp +++ b/src/token.cpp @@ -169,7 +169,7 @@ int Token::multiCompare(const char *haystack, const char *needle) bool emptyStringFound = false; bool noMatch = false; const char *needlePointer = needle; - for (; *haystack; ++haystack) + for (; *haystack && *haystack != ' '; ++haystack) { if (*haystack == '|') { @@ -260,42 +260,87 @@ bool Token::simpleMatch(const Token *tok, const char pattern[]) return true; } +int Token::firstWordEquals(const char *str, const char *word) +{ + for (;;) + { + if (*str == ' ' && *word == 0) + return 0; + else if (*str != *word) + return 1; + else if (*str == 0) + break; + + ++str; + ++word; + } + + return 0; +} + +const char *Token::chrInFirstWord(const char *str, char c) +{ + for (;;) + { + if (*str == ' ' || *str == 0) + return 0; + + if (*str == c) + return str; + + ++str; + } +} + +int Token::firstWordLen(const char *str) +{ + int len = 0; + for (;;) + { + if (*str == ' ' || *str == 0) + break; + + ++len; + ++str; + } + + return len; +} + bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) { const char *p = pattern; bool firstpattern = true; + bool first = true; while (*p) { + if (!first) + { + while (*p && *p != ' ') + ++p; + } + + first = false; + // Skip spaces in pattern.. while (*p == ' ') ++p; - // Extract token from pattern.. - char str[500]; - char *s = str; - while (*p && *p != ' ') - { - *s = *p; - ++s; - ++p; - } - *s = 0; - // No token => Success! - if (str[0] == 0) + if (*p == 0) return true; if (!tok) { // If we have no tokens, pattern "!!else" should return true - if (str[1] == '!' && str[0] == '!' && str[2] != '\0') + if (p[1] == '!' && p[0] == '!' && p[2] != '\0') continue; else return false; } // If we are in the first token, we skip all initial !! patterns - if (firstpattern && !tok->previous() && tok->next() && str[1] == '!' && str[0] == '!' && str[2] != '\0') + if (firstpattern && !tok->previous() && tok->next() && p[1] == '!' && p[0] == '!' && p[2] != '\0') continue; firstpattern = false; @@ -303,10 +348,10 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) // Compare the first character of the string for optimization reasons // before doing more detailed checks. bool patternIdentified = false; - if (str[0] == '%') + if (p[0] == '%') { // Any symbolname.. - if (strcmp(str, "%var%") == 0) + if (firstWordEquals(p, "%var%") == 0) { if (!tok->isName()) return false; @@ -315,7 +360,7 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) } // Type.. - if (strcmp(str, "%type%") == 0) + if (firstWordEquals(p, "%type%") == 0) { if (!tok->isName()) return false; @@ -327,12 +372,12 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) } // Accept any token - else if (strcmp(str, "%any%") == 0) + else if (firstWordEquals(p, "%any%") == 0) { patternIdentified = true; } - else if (strcmp(str, "%varid%") == 0) + else if (firstWordEquals(p, "%varid%") == 0) { if (varid == 0) { @@ -345,7 +390,7 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) patternIdentified = true; } - else if (strcmp(str, "%num%") == 0) + else if (firstWordEquals(p, "%num%") == 0) { if (!tok->isNumber()) return false; @@ -353,7 +398,7 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) patternIdentified = true; } - else if (strcmp(str, "%bool%") == 0) + else if (firstWordEquals(p, "%bool%") == 0) { if (!tok->isBoolean()) return false; @@ -361,7 +406,7 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) patternIdentified = true; } - else if (strcmp(str, "%str%") == 0) + else if (firstWordEquals(p, "%str%") == 0) { if (tok->_str[0] != '\"') return false; @@ -376,17 +421,43 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) } // [.. => search for a one-character token.. - else if (str[0] == '[' && strchr(str, ']') && tok->_str[1] == 0) + else if (p[0] == '[' && chrInFirstWord(p, ']') && tok->_str[1] == 0) { - *strrchr(str, ']') = 0; - if (strchr(str + 1, tok->_str[0]) == 0) + const char *temp = p + 1; + bool chrFound = false; + int count = 0; + while (*temp && *temp != ' ') + { + if (*temp == ']') + { + ++count; + ++temp; + continue; + } + + if (*temp == tok->_str[0]) + { + chrFound = true; + break; + } + + ++temp; + } + + if (count > 1) + { + if (tok->_str[0] == ']') + chrFound = true; + } + + if (!chrFound) return false; } // Parse multi options, such as void|int|char (accept token which is one of these 3) - else if (strchr(str, '|') && (str[0] != '|' || strlen(str) > 2)) + else if (chrInFirstWord(p, '|') && (p[0] != '|' || firstWordLen(p) > 2)) { - int res = multiCompare(str, tok->_str.c_str()); + int res = multiCompare(p, tok->_str.c_str()); if (res == 0) { // Empty alternative matches, use the same token on next round @@ -400,13 +471,13 @@ bool Token::Match(const Token *tok, const char pattern[], unsigned int varid) } // Parse "not" options. Token can be anything except the given one - else if (str[1] == '!' && str[0] == '!' && str[2] != '\0') + else if (p[1] == '!' && p[0] == '!' && p[2] != '\0') { - if (strcmp(tok->str().c_str(), &(str[2])) == 0) + if (firstWordEquals(&(p[2]), tok->str().c_str()) == 0) return false; } - else if (str != tok->_str) + else if (firstWordEquals(p, tok->_str.c_str()) != 0) return false; tok = tok->next(); diff --git a/src/token.h b/src/token.h index 6ff4a3999..383316f48 100644 --- a/src/token.h +++ b/src/token.h @@ -217,6 +217,28 @@ private: void next(Token *next); void previous(Token *previous); + /** + * Works almost like strcmp() except returns only 0 or 1 and + * if str has empty space ' ' character, that character is handled + * as if it were '\0' + */ + static int firstWordEquals(const char *str, const char *word); + + /** + * Works almost like strchr() except + * if str has empty space ' ' character, that character is handled + * as if it were '\0' + */ + static const char *chrInFirstWord(const char *str, char c); + + /** + * Works almost like strlen() except + * if str has empty space ' ' character, that character is handled + * as if it were '\0' + */ + static int firstWordLen(const char *str); + + std::string _str; bool _isName; bool _isNumber;