ValueFlow: Improved valueflow of for loop 'for (i=a; i<10; i++)' => unknown start value but end value is known

This commit is contained in:
Daniel Marjamäki 2014-03-29 20:20:22 +01:00
parent 407c9fdf9d
commit e5301b2b7a
5 changed files with 141 additions and 106 deletions

View File

@ -961,6 +961,16 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
return; return;
} }
// ValueFlow array index..
if ((declarationId > 0 && Token::Match(tok, "%varid% [", declarationId)) ||
(declarationId == 0 && Token::Match(tok, (varnames + " [").c_str()))) {
const Token *tok2 = tok;
while (tok2->str() != "[")
tok2 = tok2->next();
valueFlowCheckArrayIndex(tok2, arrayInfo);
}
// Array index.. // Array index..
if ((declarationId > 0 && Token::Match(tok, "%varid% [ %num% ]", declarationId)) || if ((declarationId > 0 && Token::Match(tok, "%varid% [ %num% ]", declarationId)) ||
(declarationId == 0 && Token::Match(tok, (varnames + " [ %num% ]").c_str()))) { (declarationId == 0 && Token::Match(tok, (varnames + " [ %num% ]").c_str()))) {
@ -1165,26 +1175,21 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
} }
} }
void CheckBufferOverrun::valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo)
void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo)
{ {
const MathLib::bigint total_size = arrayInfo.num(0) * arrayInfo.element_size(); // Taking address?
bool addressOf = false;
const Token *scope_begin = tok->previous(); {
assert(scope_begin != 0); const Token *tok2 = tok->astParent();
while (Token::Match(tok2, "%var%|.|::"))
for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { tok2 = tok2->astParent();
// Skip array declarations addressOf = Token::Match(tok2, "&") && !(tok2->astOperand1() && tok2->astOperand2());
if (Token::Match(tok, "[;{}] %type% *| %var% [") && tok->strAt(1) != "return") {
tok = tok->tokAt(3);
continue;
} }
else if (Token::Match(tok, "%varid% [", arrayInfo.declarationId())) {
// Look for errors first // Look for errors first
for (int warn = 0; warn == 0 || warn == 1; ++warn) { for (int warn = 0; warn == 0 || warn == 1; ++warn) {
// Negative index.. // Negative index..
for (const Token *tok2 = tok->next(); tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) { for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) {
const Token *index = tok2->astOperand2(); const Token *index = tok2->astOperand2();
if (!index) if (!index)
continue; continue;
@ -1204,7 +1209,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo
// Index out of bounds.. // Index out of bounds..
std::vector<ValueFlow::Value> indexes; std::vector<ValueFlow::Value> indexes;
unsigned int valuevarid = 0; unsigned int valuevarid = 0;
for (const Token *tok2 = tok->next(); indexes.size() < arrayInfo.num().size() && Token::Match(tok2, "["); tok2 = tok2->link()->next()) { for (const Token *tok2 = tok; indexes.size() < arrayInfo.num().size() && Token::Match(tok2, "["); tok2 = tok2->link()->next()) {
if (!tok2->astOperand2()) { if (!tok2->astOperand2()) {
indexes.clear(); indexes.clear();
break; break;
@ -1249,16 +1254,8 @@ void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo
if (totalElements == 0) if (totalElements == 0)
continue; continue;
const Token *tok2 = tok->previous();
while (tok2 && Token::Match(tok2->previous(), "%var% ."))
tok2 = tok2->tokAt(-2);
// just taking the address?
const bool addr(tok2 && (tok2->str() == "&" ||
Token::simpleMatch(tok2->previous(), "& (")));
// taking address of 1 past end? // taking address of 1 past end?
if (addr && totalIndex == totalElements) if (addressOf && totalIndex == totalElements)
continue; continue;
// Is totalIndex in bounds? // Is totalIndex in bounds?
@ -1283,6 +1280,26 @@ void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo
} }
} }
} }
}
void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo)
{
const MathLib::bigint total_size = arrayInfo.num(0) * arrayInfo.element_size();
const Token *scope_begin = tok->previous();
assert(scope_begin != 0);
for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) {
// Skip array declarations
if (Token::Match(tok, "[;{}] %type% *| %var% [") && tok->strAt(1) != "return") {
tok = tok->tokAt(3);
continue;
}
else if (Token::Match(tok, "%varid% [", arrayInfo.declarationId())) {
valueFlowCheckArrayIndex(tok->next(), arrayInfo);
} }
// Loop.. // Loop..

View File

@ -231,6 +231,8 @@ private:
void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname); void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname);
void writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string& functionName); void writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string& functionName);
void valueFlowCheckArrayIndex(const Token * const tok, const ArrayInfo &arrayInfo);
public: public:
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
CheckBufferOverrun c(0, settings, errorLogger); CheckBufferOverrun c(0, settings, errorLogger);

View File

@ -722,14 +722,21 @@ static void execute(const Token *expr,
static bool valueFlowForLoop1(const Token *tok, unsigned int * const varid, MathLib::bigint * const num1, MathLib::bigint * const num2) static bool valueFlowForLoop1(const Token *tok, unsigned int * const varid, MathLib::bigint * const num1, MathLib::bigint * const num2)
{ {
tok = tok->tokAt(2); tok = tok->tokAt(2);
if (!Token::Match(tok,"%type%| %var% = %num% ;")) if (!Token::Match(tok,"%type%| %var% ="))
return false; return false;
const Token * const vartok = tok->tokAt(Token::Match(tok, "%var% =") ? 0 : 1); const Token * const vartok = tok->tokAt(Token::Match(tok, "%var% =") ? 0 : 1);
if (vartok->varId() == 0U) if (vartok->varId() == 0U)
return false; return false;
*varid = vartok->varId(); *varid = vartok->varId();
*num1 = MathLib::toLongNumber(vartok->strAt(2)); const Token * const num1tok = Token::Match(vartok->tokAt(2), "%num% ;") ? vartok->tokAt(2) : nullptr;
tok = vartok->tokAt(4); if (num1tok)
*num1 = MathLib::toLongNumber(num1tok->str());
tok = vartok->tokAt(2);
while (Token::Match(tok, "%var%|%num%|%or%|+|-|*|/|&|[|]|("))
tok = (tok->str() == "(") ? tok->link()->next() : tok->next();
if (!tok || tok->str() != ";")
return false;
tok = tok->next();
const Token *num2tok = nullptr; const Token *num2tok = nullptr;
if (Token::Match(tok, "%varid% <|<=|!=", vartok->varId())) { if (Token::Match(tok, "%varid% <|<=|!=", vartok->varId())) {
tok = tok->next(); tok = tok->next();
@ -742,6 +749,8 @@ static bool valueFlowForLoop1(const Token *tok, unsigned int * const varid, Math
if (!num2tok) if (!num2tok)
return false; return false;
*num2 = MathLib::toLongNumber(num2tok ? num2tok->str() : "0") - ((tok->str()=="<=") ? 0 : 1); *num2 = MathLib::toLongNumber(num2tok ? num2tok->str() : "0") - ((tok->str()=="<=") ? 0 : 1);
if (!num1tok)
*num1 = *num2;
while (tok && tok->str() != ";") while (tok && tok->str() != ";")
tok = tok->next(); tok = tok->next();
if (!num2tok || !Token::Match(tok, "; %varid% ++ ) {", vartok->varId())) if (!num2tok || !Token::Match(tok, "; %varid% ++ ) {", vartok->varId()))

View File

@ -425,7 +425,8 @@ private:
" for (i = a; i < 100; i++)\n" " for (i = a; i < 100; i++)\n"
" sum += val[i];\n" " sum += val[i];\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: val\n", errout.str()); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: val\n"
"[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
} }
{ {

View File

@ -633,6 +633,12 @@ private:
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9)); ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
ASSERT_EQUALS(false, testValueOfX(code, 3U, 10)); ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
code = "void f(int a) {\n"
" for (int x = a; x < 10; x++)\n"
" a[x] = 0;\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
code = "void f() {\n" code = "void f() {\n"
" for (int x = 0; x < 10; x = x + 2)\n" " for (int x = 0; x < 10; x = x + 2)\n"
" a[x] = 0;\n" " a[x] = 0;\n"