Fixed #1492 (false negatives: array index out of bounds)
This commit is contained in:
parent
2dc4222c9a
commit
62d2845014
|
@ -31,6 +31,7 @@
|
|||
#include <list>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
|
||||
#include <cassert> // <- assert
|
||||
#include <cstdlib> // <- strtoul
|
||||
|
@ -809,6 +810,63 @@ void CheckBufferOverrun::checkGlobalAndLocalVariable()
|
|||
varid = tok->tokAt(varpos)->varId();
|
||||
nextTok = varpos + 5;
|
||||
}
|
||||
else if (Token::Match(tok, "%type% *| %var% [ %var% ] [;=]"))
|
||||
{
|
||||
unsigned int varpos = 1;
|
||||
if (tok->next()->str() == "*")
|
||||
++varpos;
|
||||
|
||||
// get maximum size from type
|
||||
// find where this token is defined
|
||||
const Token *index_type = Token::findmatch(_tokenizer->tokens(), "%varid%", tok->tokAt(varpos + 2)->varId());
|
||||
|
||||
index_type = index_type->previous();
|
||||
|
||||
if (index_type->str() == "char")
|
||||
{
|
||||
if (index_type->isUnsigned())
|
||||
size = UCHAR_MAX + 1;
|
||||
else if (index_type->isSigned())
|
||||
size = SCHAR_MAX + 1;
|
||||
else
|
||||
size = CHAR_MAX + 1;
|
||||
}
|
||||
else if (index_type->str() == "short")
|
||||
{
|
||||
if (index_type->isUnsigned())
|
||||
size = USHRT_MAX + 1;
|
||||
else
|
||||
size = SHRT_MAX + 1;
|
||||
}
|
||||
else if (index_type->str() == "int")
|
||||
{
|
||||
if (index_type->isUnsigned())
|
||||
size = UINT_MAX; // really UINT_MAX + 1;
|
||||
else
|
||||
size = INT_MAX + 1U;
|
||||
}
|
||||
else if (index_type->str() == "long")
|
||||
{
|
||||
if (index_type->isUnsigned())
|
||||
{
|
||||
if (index_type->isLong())
|
||||
size = ULONG_MAX; // really ULLONG_MAX + 1;
|
||||
else
|
||||
size = ULONG_MAX; // really ULONG_MAX + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index_type->isLong())
|
||||
size = ULONG_MAX; // really LLONG_MAX + 1;
|
||||
else
|
||||
size = LONG_MAX + 1U;
|
||||
}
|
||||
}
|
||||
|
||||
type = tok->strAt(varpos - 1);
|
||||
varid = tok->tokAt(varpos)->varId();
|
||||
nextTok = varpos + 5;
|
||||
}
|
||||
else if (indentlevel > 0 && Token::Match(tok, "[*;{}] %var% = new %type% [ %num% ]"))
|
||||
{
|
||||
size = MathLib::toLongNumber(tok->strAt(6));
|
||||
|
|
|
@ -1791,8 +1791,6 @@ bool CheckClass::isMemberFunc(const Token *tok)
|
|||
return true;
|
||||
else if (Token::Match(tok, "operator %any% ("))
|
||||
return true;
|
||||
else if (Token::Match(tok, "%type%"))
|
||||
tok = tok->next();
|
||||
|
||||
while (Token::Match(tok, ":: %type%"))
|
||||
tok = tok->tokAt(2);
|
||||
|
|
|
@ -315,14 +315,12 @@ void CheckOther::checkUnsignedDivision()
|
|||
{
|
||||
if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]"))
|
||||
{
|
||||
const std::string type = tok->strAt(1);
|
||||
if (type == "char" || type == "short" || type == "int")
|
||||
if (tok->tokAt(1)->isUnsigned())
|
||||
varsign[tok->tokAt(2)->varId()] = 'u';
|
||||
else
|
||||
varsign[tok->tokAt(2)->varId()] = 's';
|
||||
}
|
||||
|
||||
else if (Token::Match(tok, "[{};(,] unsigned %type% %var% [;=,)]"))
|
||||
varsign[tok->tokAt(3)->varId()] = 'u';
|
||||
|
||||
else if (!Token::Match(tok, "[).]") &&
|
||||
Token::Match(tok->next(), "%var% / %var%") &&
|
||||
tok->tokAt(1)->varId() != 0 &&
|
||||
|
@ -906,8 +904,12 @@ void CheckOther::checkCharVariable()
|
|||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
// Declaring the variable..
|
||||
if (Token::Match(tok, "[{};(,] signed| char %var% [;=,)]"))
|
||||
if (Token::Match(tok, "[{};(,] char %var% [;=,)]"))
|
||||
{
|
||||
// Check for unsigned char
|
||||
if (tok->tokAt(1)->isUnsigned())
|
||||
continue;
|
||||
|
||||
// Set tok to point to the variable name
|
||||
tok = tok->tokAt(2);
|
||||
if (tok->str() == "char")
|
||||
|
|
|
@ -32,6 +32,9 @@ Token::Token(Token **t) :
|
|||
_isName(false),
|
||||
_isNumber(false),
|
||||
_isBoolean(false),
|
||||
_isUnsigned(false),
|
||||
_isSigned(false),
|
||||
_isLong(false),
|
||||
_varId(0),
|
||||
_next(0),
|
||||
_previous(0),
|
||||
|
@ -557,7 +560,7 @@ size_t Token::getStrLength(const Token *tok)
|
|||
bool Token::isStandardType() const
|
||||
{
|
||||
bool ret = false;
|
||||
const char *type[] = {"bool", "char", "short", "int", "long", "float", "double", "size_t", 0};
|
||||
const char *type[] = {"bool", "char", "short", "int", "long", "float", "double", "size_t", "__int64", 0};
|
||||
for (int i = 0; type[i]; i++)
|
||||
ret |= (_str == type[i]);
|
||||
return ret;
|
||||
|
|
27
lib/token.h
27
lib/token.h
|
@ -149,6 +149,30 @@ public:
|
|||
{
|
||||
return _isBoolean;
|
||||
}
|
||||
bool isUnsigned() const
|
||||
{
|
||||
return _isUnsigned;
|
||||
}
|
||||
void isUnsigned(bool sign)
|
||||
{
|
||||
_isUnsigned = sign;
|
||||
}
|
||||
bool isSigned() const
|
||||
{
|
||||
return _isSigned;
|
||||
}
|
||||
void isSigned(bool sign)
|
||||
{
|
||||
_isSigned = sign;
|
||||
}
|
||||
bool isLong() const
|
||||
{
|
||||
return _isLong;
|
||||
}
|
||||
void isLong(bool size)
|
||||
{
|
||||
_isLong = size;
|
||||
}
|
||||
bool isStandardType() const;
|
||||
|
||||
static const Token *findmatch(const Token *tok, const char pattern[], unsigned int varId = 0);
|
||||
|
@ -341,6 +365,9 @@ private:
|
|||
bool _isName;
|
||||
bool _isNumber;
|
||||
bool _isBoolean;
|
||||
bool _isUnsigned;
|
||||
bool _isSigned;
|
||||
bool _isLong;
|
||||
unsigned int _varId;
|
||||
Token *_next;
|
||||
Token *_previous;
|
||||
|
|
|
@ -134,6 +134,13 @@ unsigned int Tokenizer::sizeOfType(const Token *type) const
|
|||
std::map<std::string, unsigned int>::const_iterator it = _typeSize.find(type->strAt(0));
|
||||
if (it == _typeSize.end())
|
||||
return 0;
|
||||
else if (type->isLong())
|
||||
{
|
||||
if (type->str() == "double")
|
||||
return sizeof(long double);
|
||||
else if (type->str() == "long")
|
||||
return sizeof(long long);
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
@ -152,6 +159,9 @@ void Tokenizer::insertTokens(Token *dest, const Token *src, unsigned int n)
|
|||
dest->fileIndex(src->fileIndex());
|
||||
dest->linenr(src->linenr());
|
||||
dest->varId(src->varId());
|
||||
dest->isUnsigned(src->isUnsigned());
|
||||
dest->isSigned(src->isSigned());
|
||||
dest->isLong(src->isLong());
|
||||
src = src->next();
|
||||
--n;
|
||||
}
|
||||
|
@ -1231,6 +1241,10 @@ bool Tokenizer::tokenize(std::istream &code, const char FileName[], const std::s
|
|||
// replace "unsigned i" with "unsigned int i"
|
||||
unsignedint();
|
||||
|
||||
// colapse compound standard types into a single token
|
||||
// unsigned long long int => long _isUnsigned=true,_isLong=true
|
||||
simplifyStdType();
|
||||
|
||||
// Use "<" comparison instead of ">"
|
||||
simplifyComparisonOrder();
|
||||
|
||||
|
@ -4305,6 +4319,59 @@ void Tokenizer::simplifyVarDecl()
|
|||
}
|
||||
}
|
||||
|
||||
void Tokenizer::simplifyStdType()
|
||||
{
|
||||
for (Token *tok = _tokens; tok; tok = tok->next())
|
||||
{
|
||||
// long unsigned => unsigned long
|
||||
if (Token::Match(tok, "long|short unsigned|signed"))
|
||||
{
|
||||
std::string temp = tok->str();
|
||||
tok->str(tok->next()->str());
|
||||
tok->next()->str(temp);
|
||||
}
|
||||
|
||||
if (!Token::Match(tok, "unsigned|signed|long|char|short|int|__int64"))
|
||||
continue;
|
||||
|
||||
// check if signed or unsigned specified
|
||||
if (Token::Match(tok, "unsigned|signed"))
|
||||
{
|
||||
bool isUnsigned = tok->str() == "unsigned";
|
||||
tok->deleteThis();
|
||||
tok->isUnsigned(isUnsigned);
|
||||
tok->isSigned(!isUnsigned);
|
||||
}
|
||||
|
||||
if (Token::Match(tok, "__int64"))
|
||||
{
|
||||
tok->str("long");
|
||||
tok->isLong(true);
|
||||
}
|
||||
else if (Token::Match(tok, "long"))
|
||||
{
|
||||
if (Token::Match(tok->next(), "long"))
|
||||
{
|
||||
tok->isLong(true);
|
||||
tok->deleteNext();
|
||||
}
|
||||
|
||||
if (Token::Match(tok->next(), "int"))
|
||||
tok->deleteNext();
|
||||
else if (Token::Match(tok->next(), "double"))
|
||||
{
|
||||
tok->str("double");
|
||||
tok->isLong(true);
|
||||
tok->deleteNext();
|
||||
}
|
||||
}
|
||||
else if (Token::Match(tok, "short"))
|
||||
{
|
||||
if (Token::Match(tok->next(), "int"))
|
||||
tok->deleteNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tokenizer::unsignedint()
|
||||
{
|
||||
|
|
|
@ -170,6 +170,12 @@ public:
|
|||
*/
|
||||
void unsignedint();
|
||||
|
||||
/**
|
||||
* Colapse compound standard types into a single token.
|
||||
* unsigned long long int => long _isUnsigned=true,_isLong=true
|
||||
*/
|
||||
void simplifyStdType();
|
||||
|
||||
/**
|
||||
* Simplify question mark - colon operator
|
||||
* Example: 0 ? (2/0) : 0 => 0
|
||||
|
|
|
@ -93,6 +93,7 @@ private:
|
|||
TEST_CASE(array_index_21);
|
||||
TEST_CASE(array_index_22);
|
||||
TEST_CASE(array_index_23);
|
||||
TEST_CASE(array_index_24); // ticket #1492
|
||||
TEST_CASE(array_index_multidim);
|
||||
TEST_CASE(array_index_switch_in_for);
|
||||
TEST_CASE(array_index_calculation);
|
||||
|
@ -757,6 +758,42 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:4]: (error) Array 'c[10]' index 8388608 out of bounds\n", errout.str());
|
||||
}
|
||||
|
||||
void array_index_24()
|
||||
{
|
||||
// ticket #1492
|
||||
check("void f(signed char n) {\n"
|
||||
" int a[n];\n" // n <= SCHAR_MAX
|
||||
" a[-1] = 0;\n" // negative index
|
||||
" a[128] = 0;\n" // 128 > SCHAR_MAX
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[128]' index -1 out of bounds\n"
|
||||
"[test.cpp:4]: (error) Array 'a[128]' index 128 out of bounds\n", errout.str());
|
||||
|
||||
check("void f(unsigned char n) {\n"
|
||||
" int a[n];\n" // n <= UCHAR
|
||||
" a[-1] = 0;\n" // negative index
|
||||
" a[256] = 0;\n" // 256 > UCHAR_MAX
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[256]' index -1 out of bounds\n"
|
||||
"[test.cpp:4]: (error) Array 'a[256]' index 256 out of bounds\n", errout.str());
|
||||
|
||||
check("void f(short n) {\n"
|
||||
" int a[n];\n" // n <= SHRT_MAX
|
||||
" a[-1] = 0;\n" // negative index
|
||||
" a[32768] = 0;\n" // 32768 > SHRT_MAX
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' index -1 out of bounds\n"
|
||||
"[test.cpp:4]: (error) Array 'a[32768]' index 32768 out of bounds\n", errout.str());
|
||||
|
||||
check("void f(unsigned short n) {\n"
|
||||
" int a[n];\n" // n <= USHRT_MAX
|
||||
" a[-1] = 0;\n" // negative index
|
||||
" a[65536] = 0;\n" // 65536 > USHRT_MAX
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[65536]' index -1 out of bounds\n"
|
||||
"[test.cpp:4]: (error) Array 'a[65536]' index 65536 out of bounds\n", errout.str());
|
||||
}
|
||||
|
||||
void array_index_multidim()
|
||||
{
|
||||
check("void f()\n"
|
||||
|
|
|
@ -102,6 +102,7 @@ private:
|
|||
TEST_CASE(const12); // ticket #1552
|
||||
TEST_CASE(const13); // ticket #1519
|
||||
TEST_CASE(const14);
|
||||
TEST_CASE(const15);
|
||||
TEST_CASE(constoperator); // operator< can often be const
|
||||
TEST_CASE(constincdec); // increment/decrement => non-const
|
||||
TEST_CASE(constReturnReference);
|
||||
|
@ -2671,6 +2672,48 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:3]: (style) The function 'A::foo' can be const\n", errout.str());
|
||||
}
|
||||
|
||||
void const15()
|
||||
{
|
||||
checkConst("class Fred {\n"
|
||||
" unsigned long long int a;\n"
|
||||
" unsigned long long int getA() { return a; }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (style) The function 'Fred::getA' can be const\n", errout.str());
|
||||
|
||||
// constructors can't be const..
|
||||
checkConst("class Fred {\n"
|
||||
" unsigned long long int a;\n"
|
||||
"public:\n"
|
||||
" Fred() { }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// assignment through |=..
|
||||
checkConst("class Fred {\n"
|
||||
" unsigned long long int a;\n"
|
||||
" unsigned long long int setA() { a |= true; }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// functions with a function call can't be const..
|
||||
checkConst("class foo\n"
|
||||
"{\n"
|
||||
"public:\n"
|
||||
" unsigned long long int x;\n"
|
||||
" void b() { a(); }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// static functions can't be const..
|
||||
checkConst("class foo\n"
|
||||
"{\n"
|
||||
"public:\n"
|
||||
" static unsigned long long int get()\n"
|
||||
" { return 0; }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
// increment/decrement => not const
|
||||
void constincdec()
|
||||
{
|
||||
|
|
|
@ -72,6 +72,7 @@ private:
|
|||
TEST_CASE(sizeof15);
|
||||
TEST_CASE(sizeof16);
|
||||
TEST_CASE(sizeof17);
|
||||
TEST_CASE(sizeof18);
|
||||
TEST_CASE(casting);
|
||||
|
||||
TEST_CASE(strlen1);
|
||||
|
@ -249,6 +250,15 @@ private:
|
|||
{
|
||||
if (tok != tokenizer.tokens())
|
||||
ret += " ";
|
||||
if (!simplify)
|
||||
{
|
||||
if (tok->isUnsigned())
|
||||
ret += "unsigned ";
|
||||
else if (tok->isSigned())
|
||||
ret += "signed ";
|
||||
}
|
||||
if (tok->isLong())
|
||||
ret += "long ";
|
||||
ret += tok->str();
|
||||
}
|
||||
|
||||
|
@ -1080,6 +1090,123 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void sizeof18()
|
||||
{
|
||||
if (sizeof(short int) == 2)
|
||||
{
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(short int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 2 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(unsigned short int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 2 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(short unsigned int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 2 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(signed short int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 2 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeof(long long) == 8)
|
||||
{
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(long long);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(signed long long);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(unsigned long long);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(long unsigned long);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(long long int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(signed long long int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(unsigned long long int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
{
|
||||
const char code[] = "void f()\n"
|
||||
"{\n"
|
||||
" sizeof(long unsigned long int);\n"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("void f ( ) { 8 ; }", tok(code));
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void casting()
|
||||
{
|
||||
{
|
||||
|
|
|
@ -231,6 +231,15 @@ private:
|
|||
std::ostringstream ostr;
|
||||
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (!simplify)
|
||||
{
|
||||
if (tok->isUnsigned())
|
||||
ostr << "unsigned ";
|
||||
else if (tok->isSigned())
|
||||
ostr << "signed ";
|
||||
}
|
||||
if (tok->isLong())
|
||||
ostr << "long ";
|
||||
ostr << tok->str();
|
||||
|
||||
// Append newlines
|
||||
|
@ -2923,7 +2932,15 @@ private:
|
|||
tokenizer.simplifyFunctionPointers();
|
||||
std::ostringstream ostr;
|
||||
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (tok->isUnsigned())
|
||||
ostr << " unsigned";
|
||||
else if (tok->isSigned())
|
||||
ostr << " signed";
|
||||
if (tok->isLong())
|
||||
ostr << " long";
|
||||
ostr << (tok->isName() ? " " : "") << tok->str();
|
||||
}
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ private:
|
|||
" func();\n"
|
||||
" } while(a--);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS(std::string(""), errout.str());
|
||||
ASSERT_EQUALS(std::string("[test.cpp:2]: (style) Unused variable: z\n"), errout.str());
|
||||
}
|
||||
|
||||
void localvarStruct4()
|
||||
|
|
Loading…
Reference in New Issue