CheckBufferOverrun: Removed old for-loop handling. This is handled through ValueFlow from now on.

This commit is contained in:
Daniel Marjamäki 2014-06-27 06:46:42 +02:00
parent b6a40fceb7
commit 6c8558c112
4 changed files with 30 additions and 524 deletions

View File

@ -262,315 +262,7 @@ static bool bailoutIfSwitch(const Token *tok, const unsigned int varid)
// No bailout stuff found => return false // No bailout stuff found => return false
return false; return false;
} }
//---------------------------------------------------------------------------
/**
* Parse for loop initialization statement. Look for a counter variable
* \param tok [in] first token inside the parentheses
* \param varid [out] varid of counter variable
* \param varname [out] name of counter variable
* \param init_value [out] init value of counter variable
* \return success => pointer to the for loop condition. fail => 0. If 0 is returned and varname has been set then there is
* a missing varid for the counter variable
*/
static const Token *for_init(const Token *tok, unsigned int &varid, std::string &varname, std::string &init_value)
{
if (Token::Match(tok, "%var% = %any% ;")) {
if (tok->tokAt(2)->isNumber()) {
init_value = tok->strAt(2);
}
varid = tok->varId();
varname = tok->str();
if (varid == 0)
return 0; // failed
tok = tok->tokAt(4);
} else if (Token::Match(tok, "%type% %var% = %any% ;")) {
if (tok->tokAt(3)->isNumber()) {
init_value = tok->strAt(3);
}
varid = tok->next()->varId();
varname = tok->next()->str();
tok = tok->tokAt(5);
} else if (Token::Match(tok, "%type% %type% %var% = %any% ;")) {
if (tok->tokAt(4)->isNumber()) {
init_value = tok->strAt(4);
}
varid = tok->tokAt(2)->varId();
varname = tok->strAt(2);
tok = tok->tokAt(6);
} else
return 0;
if (!init_value.empty() && (Token::Match(tok, "-- %varid%", varid) || Token::Match(tok, "%varid% --", varid))) {
init_value = MathLib::subtract(init_value, "1");
}
return tok;
}
/** Parse for condition */
static bool for_condition(const Token *tok2, unsigned int varid, std::string &min_value, std::string &max_value, bool &maxMinFlipped)
{
if (Token::Match(tok2, "%varid% < %num% ;|&&|%oror%", varid) ||
Token::Match(tok2, "%varid% != %num% ; ++ %varid%", varid) ||
Token::Match(tok2, "%varid% != %num% ; %varid% ++", varid)) {
maxMinFlipped = false;
const MathLib::bigint value = MathLib::toLongNumber(tok2->strAt(2));
max_value = MathLib::toString(value - 1);
} else if (Token::Match(tok2, "%varid% <= %num% ;|&&|%oror%", varid)) {
maxMinFlipped = false;
max_value = tok2->strAt(2);
} else if (Token::Match(tok2, "%num% < %varid% ;|&&|%oror%", varid) ||
Token::Match(tok2, "%num% != %varid% ; ++ %varid%", varid) ||
Token::Match(tok2, "%num% != %varid% ; %varid% ++", varid)) {
maxMinFlipped = true;
const MathLib::bigint value = MathLib::toLongNumber(tok2->str());
max_value = min_value;
min_value = MathLib::toString(value + 1);
} else if (Token::Match(tok2, "%num% <= %varid% ;|&&|%oror%", varid)) {
maxMinFlipped = true;
max_value = min_value;
min_value = tok2->str();
} else if (Token::Match(tok2, "%varid% -- ; )", varid) ||
Token::Match(tok2, "-- %varid% ; )", varid)) {
maxMinFlipped = true;
max_value = min_value;
min_value = (tok2->str() == "--") ? "1" : "0";
} else {
// parse condition
while (tok2 && tok2->str() != ";") {
if (tok2->str() == "(")
tok2 = tok2->link();
else if (tok2->str() == ")") // unexpected ")" => break
break;
if (tok2->str() == "&&" || tok2->str() == "||") {
if (for_condition(tok2->next(), varid, min_value, max_value, maxMinFlipped))
return true;
}
tok2 = tok2->next();
}
return false;
}
return true;
}
/**
* calculate maximum value of loop variable
* @param stepvalue token that contains the step value
* @param min_value the minimum value of loop variable
* @param max_value maximum value of the loop variable
*/
static bool for_maxvalue(const Token * const stepvalue, const std::string &min_value, std::string &max_value)
{
if (!MathLib::isInt(stepvalue->str()))
return false;
// We have for example code: "for(i=2;i<22;i+=6)
// We can calculate that max value for i is 20, not 21
// 21-2 = 19
// 19/6 = 3
// 6*3+2 = 20
const MathLib::bigint num = MathLib::toLongNumber(stepvalue->str());
MathLib::bigint max = MathLib::toLongNumber(max_value);
const MathLib::bigint min = MathLib::toLongNumber(min_value);
max = ((max - min) / num) * num + min;
max_value = MathLib::toString(max);
return true;
}
/**
* Parse the third sub-statement in for head
* \param tok first token
* \param varid variable id of counter
* \param min_value min value of counter
* \param max_value max value of counter
* \param maxMinFlipped counting from max to min
*/
static bool for3(const Token * const tok,
unsigned int varid,
std::string &min_value,
std::string &max_value,
const bool maxMinFlipped)
{
assert(tok != nullptr);
if (Token::Match(tok, "%varid% = %num% + %varid% )", varid)) {
if (!for_maxvalue(tok->tokAt(2), min_value, max_value))
return false;
} else if (Token::Match(tok, "%varid% = %varid% + %num% )", varid)) {
if (!for_maxvalue(tok->tokAt(4), min_value, max_value))
return false;
} else if (Token::Match(tok, "%varid% = %num% - %varid% )", varid)) {
if (!for_maxvalue(tok->tokAt(2), min_value, max_value))
return false;
} else if (Token::Match(tok, "%varid% = %varid% - %num% )", varid)) {
if (!for_maxvalue(tok->tokAt(4), min_value, max_value))
return false;
} else if (Token::Match(tok, "--| %varid% --| )", varid)) {
if (!maxMinFlipped && MathLib::toLongNumber(min_value) < MathLib::toLongNumber(max_value)) {
// Code relies on the fact that integer will overflow:
// for (unsigned int i = 3; i < 5; --i)
// Set min value in this case to zero.
max_value = min_value;
min_value = "0";
}
} else if (! Token::Match(tok, "++| %varid% ++| )", varid)) {
return false;
}
return true;
}
/**
* Check is the counter variable increased elsewhere inside the loop or used
* for anything else except reading
* \param tok1 first token of for-body
* \param varid counter variable id
* \return bailout needed => true
*/
static bool for_bailout(const Token * const tok1, unsigned int varid)
{
for (const Token *loopTok = tok1; loopTok && loopTok != tok1->link(); loopTok = loopTok->next()) {
if (loopTok->varId() == varid) {
// Counter variable used inside loop
if (Token::Match(loopTok->next(), "++|--|=") ||
(loopTok->previous()->type() == Token::eIncDecOp)) {
return true;
}
}
}
return false;
}
void CheckBufferOverrun::parse_for_body(const Token *tok, const ArrayInfo &arrayInfo, const std::string &strindex, bool condition_out_of_bounds, unsigned int counter_varid, const std::string &min_counter_value, const std::string &max_counter_value)
{
unsigned int arrayInfoDeclarationId = arrayInfo.declarationId();
const std::string pattern = (arrayInfoDeclarationId ? std::string("%varid%") : arrayInfo.varname()) + " [ " + strindex + " ]";
for (const Token* tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) {
// TestBufferOverrun::array_index_for_question
if (tok2->str() == "?") {
// does condition check counter variable?
bool usesCounter = false;
const Token *tok3 = tok2->previous();
while (Token::Match(tok3, "%comp%|%num%|%var%|)")) {
if (tok3->str() == strindex) {
usesCounter = true;
break;
}
tok3 = tok3->previous();
}
// If strindex is used in the condition then skip the
// conditional expressions
if (usesCounter) {
while (tok2 && !Token::Match(tok2, "[)],;]")) {
if (tok2->str() == "(" || tok2->str() == "[")
tok2 = tok2->link();
tok2 = tok2->next();
}
if (!tok2)
break;
continue;
}
}
if (Token::simpleMatch(tok2, "for (") && Token::simpleMatch(tok2->next()->link(), ") {")) {
const Token *endpar = tok2->next()->link();
const Token *startbody = endpar->next();
const Token *endbody = startbody->link();
tok2 = endbody;
continue;
}
if (Token::Match(tok2, "if|switch")) {
if (bailoutIfSwitch(tok2, arrayInfoDeclarationId))
break;
}
if (condition_out_of_bounds && Token::Match(tok2, pattern.c_str(), arrayInfoDeclarationId)) {
bufferOverrunError(tok2, arrayInfo.varname());
break;
}
else if (arrayInfoDeclarationId && tok2->varId() && counter_varid > 0 && !min_counter_value.empty() && !max_counter_value.empty()) {
// Is the loop variable used to calculate the array index?
// In this scope it is determined if such calculated
// array indexes are out of bounds.
// Only the minimum and maximum results of the calculation is
// determined
// Minimum calculated array index
int min_index = 0;
// Maximum calculated array index
int max_index = 0;
if (Token::Match(tok2, "%varid% [ %var% +|-|*|/ %num% ]", arrayInfoDeclarationId) &&
tok2->tokAt(2)->varId() == counter_varid) {
// operator: +-*/
const char action = tok2->strAt(3)[0];
// second operator
const std::string &second(tok2->strAt(4));
//printf("min_index: %s %c %s\n", min_counter_value.c_str(), action, second.c_str());
//printf("max_index: %s %c %s\n", max_counter_value.c_str(), action, second.c_str());
min_index = std::atoi(MathLib::calculate(min_counter_value, second, action).c_str());
max_index = std::atoi(MathLib::calculate(max_counter_value, second, action).c_str());
} else if (Token::Match(tok2, "%varid% [ %num% +|-|*|/ %var% ]", arrayInfoDeclarationId) &&
tok2->tokAt(4)->varId() == counter_varid) {
// operator: +-*/
const char action = tok2->strAt(3)[0];
// first operand
const std::string &first(tok2->strAt(2));
//printf("min_index: %s %c %s\n", first.c_str(), action, min_counter_value.c_str());
//printf("max_index: %s %c %s\n", first.c_str(), action, max_counter_value.c_str());
min_index = std::atoi(MathLib::calculate(first, min_counter_value, action).c_str());
max_index = std::atoi(MathLib::calculate(first, max_counter_value, action).c_str());
}
else {
continue;
}
//printf("min_index = %d, max_index = %d, size = %d\n", min_index, max_index, size);
if (min_index < 0 || max_index < 0) {
std::vector<MathLib::bigint> indexes;
indexes.push_back(std::min(min_index, max_index));
arrayIndexOutOfBoundsError(tok2, arrayInfo, indexes);
}
// skip 0 length arrays
if (arrayInfo.num(0) == 0)
;
// taking address.
else if (tok2->previous()->str() == "&" && max_index == arrayInfo.num(0))
;
else if (arrayInfo.num(0) && (min_index >= arrayInfo.num(0) || max_index >= arrayInfo.num(0))) {
std::vector<MathLib::bigint> indexes;
indexes.push_back(std::max(min_index, max_index));
arrayIndexOutOfBoundsError(tok2, arrayInfo, indexes);
}
}
}
}
void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int par, const ArrayInfo &arrayInfo, const std::list<const Token *>& callstack) void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int par, const ArrayInfo &arrayInfo, const std::list<const Token *>& callstack)
{ {
@ -790,132 +482,6 @@ void CheckBufferOverrun::checkFunctionCall(const Token *tok, const ArrayInfo &ar
} }
} }
void CheckBufferOverrun::checkScopeForBody(const Token *tok, const ArrayInfo &arrayInfo, bool &bailout)
{
bailout = false;
// Check if there is a break in the body..
{
const Token *bodyStart = tok->next()->link()->next();
const Token *bodyEnd = bodyStart->link();
if (Token::findsimplematch(bodyStart, "break ;", bodyEnd))
return;
}
const Token *tok2 = tok->tokAt(2);
const MathLib::bigint size = arrayInfo.num(0);
std::string counter_name;
unsigned int counter_varid = 0;
std::string counter_init_value;
tok2 = for_init(tok2, counter_varid, counter_name, counter_init_value);
if (tok2 == 0 && !counter_name.empty())
_tokenizer->getSymbolDatabase()->debugMessage(tok, "for loop variable \'" + counter_name + "\' has varid 0.");
if (tok2 == 0 || counter_varid == 0)
return;
bool maxMinFlipped = false;
std::string min_counter_value = counter_init_value;
std::string max_counter_value;
if (!for_condition(tok2, counter_varid, min_counter_value, max_counter_value, maxMinFlipped)) {
// Can't understand the condition. Check that the start value
// is used correctly
const Token * const startForScope = tok->next()->link()->next();
if (!for_bailout(startForScope, counter_varid)) {
// Get index variable and stopsize.
bool condition_out_of_bounds = bool(size > 0);
if (MathLib::toLongNumber(counter_init_value) < size)
condition_out_of_bounds = false;
parse_for_body(startForScope, arrayInfo, counter_name, condition_out_of_bounds, counter_varid, counter_init_value, counter_init_value);
}
return;
}
// Get index variable and stopsize.
bool condition_out_of_bounds = bool(size > 0);
if (MathLib::toLongNumber(max_counter_value) < size)
condition_out_of_bounds = false;
// Goto the end of the condition
while (tok2 && tok2->str() != ";") {
if (tok2->str() == "(")
tok2 = tok2->link();
else if (tok2->str() == ")") // unexpected ")" => break
break;
tok2 = tok2->next();
}
if (!tok2 || tok2->str() != ";")
return;
const bool hasFor3 = tok2->next()->str() != ")";
if (hasFor3 && !for3(tok2->next(), counter_varid, min_counter_value, max_counter_value, maxMinFlipped))
return;
if (Token::Match(tok2->next(), "%var% =") && MathLib::toLongNumber(max_counter_value) < size)
condition_out_of_bounds = false;
// Goto the end parentheses of the for-statement: "for (x; y; z)" ..
tok2 = tok->next()->link();
if (!tok2 || !tok2->tokAt(5)) {
bailout = true;
return;
}
// Check is the counter variable increased elsewhere inside the loop or used
// for anything else except reading
if (for_bailout(tok2->next(), counter_varid)) {
bailout = true;
return;
}
parse_for_body(tok2->next(), arrayInfo, counter_name, condition_out_of_bounds, counter_varid, min_counter_value, max_counter_value);
}
void CheckBufferOverrun::arrayIndexInForLoop(const Token *tok, const ArrayInfo &arrayInfo)
{
const MathLib::bigint size = arrayInfo.num(0);
const Token *tok3 = tok->tokAt(2);
std::string counter_name;
unsigned int counter_varid = 0;
std::string counter_init_value;
tok3 = for_init(tok3, counter_varid, counter_name, counter_init_value);
if (tok3 == 0 && !counter_name.empty())
_tokenizer->getSymbolDatabase()->debugMessage(tok, "for loop variable \'" + counter_name + "\' has varid 0.");
if (tok3 == 0 || counter_varid == 0)
return;
bool maxMinFlipped = false;
std::string min_counter_value = counter_init_value;
std::string max_counter_value;
if (!for_condition(tok3, counter_varid, min_counter_value, max_counter_value, maxMinFlipped))
return;
const MathLib::bigint max_value = MathLib::toLongNumber(max_counter_value);
// Skip condition
while (tok3 && tok3->str() != ";")
tok3 = tok3->next();
if (max_value > size && Token::simpleMatch(tok3, "; ) {")) {
const Token * const endToken = tok3->linkAt(2);
const Token *useToken = nullptr;
bool incrementInLoop = false;
for (const Token *loopTok = tok3->tokAt(3); loopTok != endToken; loopTok = loopTok->next()) {
if (Token::Match(loopTok, "%varid% [ %var% ++| ]", arrayInfo.declarationId()) && loopTok->tokAt(2)->varId() == counter_varid)
useToken = loopTok;
if (Token::Match(loopTok, "%varid% ++", counter_varid))
incrementInLoop = true;
}
if ((useToken != nullptr) && incrementInLoop)
bufferOverrunError(useToken, arrayInfo.varname());
}
}
void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo) void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo)
{ {
const MathLib::bigint size = arrayInfo.num(0); const MathLib::bigint size = arrayInfo.num(0);
@ -1054,18 +620,6 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
checkFunctionParameter(*tok, 2, arrayInfo, callstack); checkFunctionParameter(*tok, 2, arrayInfo, callstack);
} }
// Loop..
if (Token::simpleMatch(tok, "for (")) {
/*
const ArrayInfo arrayInfo1(declarationId, varnames, (unsigned int)size, (unsigned int)total_size);
bool bailout = false;
checkScopeForBody(tok, arrayInfo1, bailout);
if (bailout)
break;
*/
continue;
}
// Writing data into array.. // Writing data into array..
if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", declarationId)) || if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", declarationId)) ||
(declarationId == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %str% )").c_str()))) { (declarationId == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %str% )").c_str()))) {
@ -1276,16 +830,6 @@ void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo
continue; continue;
else if (Token::Match(tok, "%var% (")) { else if (Token::Match(tok, "%var% (")) {
// Loop..
if (Token::simpleMatch(tok, "for (")) {
bool bailout = false;
arrayIndexInForLoop(tok, arrayInfo);
checkScopeForBody(tok, arrayInfo, bailout);
if (bailout)
break;
continue;
}
// Check function call.. // Check function call..
checkFunctionCall(tok, arrayInfo, std::list<const Token*>()); checkFunctionCall(tok, arrayInfo, std::list<const Token*>());

View File

@ -180,12 +180,6 @@ public:
/** Check for buffer overruns */ /** Check for buffer overruns */
void checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo); void checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo);
/** Check scope helper function - parse for body */
void checkScopeForBody(const Token *tok, const ArrayInfo &arrayInfo, bool &bailout);
/** Helper function used when parsing for-loops */
void parse_for_body(const Token *tok2, const ArrayInfo &arrayInfo, const std::string &strindex, bool condition_out_of_bounds, unsigned int counter_varid, const std::string &min_counter_value, const std::string &max_counter_value);
/** Check readlink or readlinkat() buffer usage */ /** Check readlink or readlinkat() buffer usage */
void checkReadlinkBufferUsage(const Token *ftok, const Token *scope_begin, const unsigned int varid, const MathLib::bigint total_size); void checkReadlinkBufferUsage(const Token *ftok, const Token *scope_begin, const unsigned int varid, const MathLib::bigint total_size);
@ -208,7 +202,6 @@ public:
void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index); void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index);
void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<ValueFlow::Value> &index); void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<ValueFlow::Value> &index);
void arrayIndexInForLoop(const Token *tok, const ArrayInfo &arrayInfo);
private: private:

View File

@ -1,2 +1 @@
[samples\bufferAccessOutOfBounds\bad.c:6]: (error) Buffer is accessed out of bounds: a
[samples\bufferAccessOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds. [samples\bufferAccessOutOfBounds\bad.c:6]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.

View File

@ -166,8 +166,7 @@ private:
TEST_CASE(buffer_overrun_21); TEST_CASE(buffer_overrun_21);
TEST_CASE(buffer_overrun_22); // #3124 TEST_CASE(buffer_overrun_22); // #3124
TEST_CASE(buffer_overrun_23); // #3153 TEST_CASE(buffer_overrun_23); // #3153
TEST_CASE(buffer_overrun_24); // #4106 TEST_CASE(buffer_overrun_24); // index variable is changed in for-loop
TEST_CASE(buffer_overrun_25); // #4096
TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault) TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault)
TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault)
TEST_CASE(buffer_overrun_28); // Out of bound char array access TEST_CASE(buffer_overrun_28); // Out of bound char array access
@ -402,8 +401,7 @@ private:
" for (i = 0; i < 100; i++)\n" " for (i = 0; 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" ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
"[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
} }
{ {
@ -414,8 +412,7 @@ private:
" for (i = 1; i < 100; i++)\n" " for (i = 1; 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" ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
"[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
} }
@ -427,8 +424,7 @@ 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" ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
"[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str());
} }
{ {
@ -460,8 +456,7 @@ private:
" a[i] = 0;\n" " a[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout.str());
"[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout.str());
} }
} }
@ -832,8 +827,7 @@ private:
" for (int i = 0; i < 4; i+=2)\n" " for (int i = 0; i < 4; i+=2)\n"
" a[i] = 0;\n" " a[i] = 0;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str());
"[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str());
check("void f() {\n" // #4398 check("void f() {\n" // #4398
" int a[2];\n" " int a[2];\n"
@ -1049,8 +1043,7 @@ private:
" for (int i = 3; 0 <= i; i--)\n" " for (int i = 3; 0 <= i; i--)\n"
" a[i] = i;\n" " a[i] = i;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout.str());
check("void f()\n" check("void f()\n"
"{\n" "{\n"
@ -1068,8 +1061,7 @@ private:
" for (int i = 0; i < 10; i++)\n" " for (int i = 0; i < 10; i++)\n"
" a[i-1] = a[i];\n" " a[i-1] = a[i];\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array index -1 is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array index -1 is out of bounds.\n", errout.str());
} }
void array_index_28() { void array_index_28() {
@ -1533,7 +1525,7 @@ private:
" printf(\"files(%i): %s\n\", 3-i, buffer[3-i]);\n" " printf(\"files(%i): %s\n\", 3-i, buffer[3-i]);\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Array 'buffer[3]' accessed at index 3, which is out of bounds.\n", errout.str()); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'buffer[3]' accessed at index 3, which is out of bounds.\n", "", errout.str());
check("void f() {\n" check("void f() {\n"
" int buffer[9];\n" " int buffer[9];\n"
@ -1542,8 +1534,7 @@ private:
" buffer[i] = i;\n" " buffer[i] = i;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: buffer\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buffer[9]' accessed at index 9, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'buffer[9]' accessed at index 9, which is out of bounds.\n", errout.str());
// Correct access limits -> i from 9 to 0 // Correct access limits -> i from 9 to 0
check("void f() {\n" check("void f() {\n"
@ -1780,8 +1771,7 @@ private:
" data[i] = 0;\n" " data[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout.str());
check("void f()\n" check("void f()\n"
"{\n" "{\n"
@ -1827,8 +1817,7 @@ private:
" data[x] = 0;\n" " data[x] = 0;\n"
" }" " }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str());
check("void f() {\n" check("void f() {\n"
" char data[2];\n" " char data[2];\n"
@ -1837,8 +1826,7 @@ private:
" data[x] = 0;\n" " data[x] = 0;\n"
" }" " }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str());
check("void f() {\n" check("void f() {\n"
" char data[2];\n" " char data[2];\n"
@ -1847,8 +1835,7 @@ private:
" data[x] = 0;\n" " data[x] = 0;\n"
" }" " }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str());
check("void f() {\n" check("void f() {\n"
" char data[2];\n" " char data[2];\n"
@ -1857,8 +1844,7 @@ private:
" data[x] = 0;\n" " data[x] = 0;\n"
" }" " }"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: data\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str());
} }
void array_index_for_continue() { void array_index_for_continue() {
@ -1896,8 +1882,7 @@ private:
" a[i] = 0;\n" " a[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str());
"[test.cpp:6]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str());
// Ticket #2385 - No false positive // Ticket #2385 - No false positive
check("void f() {\n" check("void f() {\n"
@ -1918,8 +1903,7 @@ private:
" a[i] = 0;\n" " a[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str());
"[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str());
} }
void array_index_for_neq() { void array_index_for_neq() {
@ -1930,8 +1914,7 @@ private:
" a[i] = 0;\n" " a[i] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[5]' accessed at index 9, which is out of bounds.\n",
"[test.cpp:4]: (error) Array 'a[5]' accessed at index 9, which is out of bounds.\n",
errout.str()); errout.str());
} }
@ -1951,8 +1934,7 @@ private:
" some_condition ? 0 : a[i-1];\n" " some_condition ? 0 : a[i-1];\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n" ASSERT_EQUALS("[test.cpp:4]: (error) Array index -1 is out of bounds.\n", errout.str());
"[test.cpp:4]: (error) Array index -1 is out of bounds.\n", errout.str());
check("void f() {\n" check("void f() {\n"
" int a[10];\n" " int a[10];\n"
@ -1961,8 +1943,7 @@ private:
" a[i-1] = 0;\n" " a[i-1] = 0;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array index -1 is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array index -1 is out of bounds.\n", errout.str());
} }
void array_index_for_varid0() { // #4228: No varid for counter variable void array_index_for_varid0() { // #4228: No varid for counter variable
@ -2284,8 +2265,7 @@ private:
" for (i = 0; i <= 10; ++i)\n" " for (i = 0; i <= 10; ++i)\n"
" a[i] = 0;\n" " a[i] = 0;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: a\n" ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str());
"[test.cpp:7]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str());
} }
@ -2296,8 +2276,7 @@ private:
" for (int i = 0; i < 8; ++i)\n" " for (int i = 0; i < 8; ++i)\n"
" p[i] = 0;\n" " p[i] = 0;\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: p\n" ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[2]' accessed at index 7, which is out of bounds.\n", errout.str());
"[test.cpp:5]: (error) Array 'p[2]' accessed at index 7, which is out of bounds.\n", errout.str());
// No false positive // No false positive
check("void foo(int x, int y)\n" check("void foo(int x, int y)\n"
@ -2627,8 +2606,7 @@ private:
" for (size_t i = 0; i <= 4; i++)\n" " for (size_t i = 0; i <= 4; i++)\n"
" dst[i] = src[i];\n" " dst[i] = src[i];\n"
"} } }\n"); "} } }\n");
ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: dst\n" ASSERT_EQUALS("[test.cpp:6]: (error) Array 'dst[4]' accessed at index 4, which is out of bounds.\n", errout.str());
"[test.cpp:6]: (error) Array 'dst[4]' accessed at index 4, which is out of bounds.\n", errout.str());
} }
void buffer_overrun_22() { // ticket #3124 void buffer_overrun_22() { // ticket #3124
@ -2672,7 +2650,8 @@ private:
} }
void buffer_overrun_24() { // ticket #4106 void buffer_overrun_24() { // index variable is changed in for-loop
// ticket #4106
check("void main() {\n" check("void main() {\n"
" int array[] = {1,2};\n" " int array[] = {1,2};\n"
" int x = 0;\n" " int x = 0;\n"
@ -2680,17 +2659,9 @@ private:
" x += array[i];\n" " x += array[i];\n"
" i++; }\n" " i++; }\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: array\n", errout.str()); TODO_ASSERT_EQUALS("error", "", errout.str());
check("void main() {\n" // ticket #4096
" int array[] = {1,2};\n"
" for( int i = 0; i<6; ) {\n"
" i++; }\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void buffer_overrun_25() { // ticket #4096
check("void main() {\n" check("void main() {\n"
" int array[] = {1,2};\n" " int array[] = {1,2};\n"
" int x = 0;\n" " int x = 0;\n"
@ -2698,8 +2669,7 @@ private:
" x += array[i++];\n" " x += array[i++];\n"
" }\n" " }\n"
"}"); "}");
TODO_ASSERT_EQUALS("error", "", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: array\n", errout.str());
} }
void buffer_overrun_26() { // ticket #4432 (segmentation fault) void buffer_overrun_26() { // ticket #4432 (segmentation fault)