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
This commit is contained in:
Reijo Tomperi 2009-08-16 23:28:17 +03:00
parent 232f62f98a
commit 36a8e8afba
2 changed files with 124 additions and 31 deletions

View File

@ -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();

View File

@ -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;