Fixed #1190 (array index out of bounds when index variable is assigned in a condition)
This commit is contained in:
parent
e17cce6ac4
commit
5fed938f56
2
Makefile
2
Makefile
|
@ -92,7 +92,7 @@ install: cppcheck
|
||||||
lib/checkautovariables.o: lib/checkautovariables.cpp lib/checkautovariables.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h
|
lib/checkautovariables.o: lib/checkautovariables.cpp lib/checkautovariables.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h
|
||||||
$(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkautovariables.o lib/checkautovariables.cpp
|
$(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkautovariables.o lib/checkautovariables.cpp
|
||||||
|
|
||||||
lib/checkbufferoverrun.o: lib/checkbufferoverrun.cpp lib/checkbufferoverrun.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h lib/mathlib.h
|
lib/checkbufferoverrun.o: lib/checkbufferoverrun.cpp lib/checkbufferoverrun.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h lib/mathlib.h lib/executionpath.h
|
||||||
$(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkbufferoverrun.o lib/checkbufferoverrun.cpp
|
$(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkbufferoverrun.o lib/checkbufferoverrun.cpp
|
||||||
|
|
||||||
lib/checkclass.o: lib/checkclass.cpp lib/checkclass.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h
|
lib/checkclass.o: lib/checkclass.cpp lib/checkclass.h lib/check.h lib/token.h lib/tokenize.h lib/classinfo.h lib/settings.h lib/errorlogger.h
|
||||||
|
|
|
@ -1260,3 +1260,174 @@ void CheckBufferOverrun::checkSprintfCall(const Token *tok, int size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "executionpath.h"
|
||||||
|
|
||||||
|
/// @addtogroup Checks
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
|
||||||
|
class ArrayInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** type size in bytes */
|
||||||
|
unsigned int type_size;
|
||||||
|
|
||||||
|
/** number of elements of array */
|
||||||
|
unsigned int num;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief %Check for buffer overruns (using ExecutionPath)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ExecutionPathBufferOverrun : public ExecutionPath
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Startup constructor */
|
||||||
|
ExecutionPathBufferOverrun(Check *c, const std::map<unsigned int, ArrayInfo> &arrayinfo)
|
||||||
|
: ExecutionPath(c, 0), arrayInfo(arrayinfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Copy this check */
|
||||||
|
ExecutionPath *copy()
|
||||||
|
{
|
||||||
|
return new ExecutionPathBufferOverrun(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief buffer information */
|
||||||
|
const std::map<unsigned int, ArrayInfo> &arrayInfo;
|
||||||
|
|
||||||
|
/** no implementation => compiler error if used by accident */
|
||||||
|
void operator=(const ExecutionPathBufferOverrun &);
|
||||||
|
|
||||||
|
/** internal constructor for creating extra checks */
|
||||||
|
ExecutionPathBufferOverrun(Check *c, const std::map<unsigned int, ArrayInfo> &arrayinfo, unsigned int varid_)
|
||||||
|
: ExecutionPath(c, varid_),
|
||||||
|
arrayInfo(arrayinfo)
|
||||||
|
{
|
||||||
|
// Pretend that variables are initialized to 0
|
||||||
|
// This checking is not about uninitialized variables
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
/** @brief Assign value to a variable */
|
||||||
|
static void assign_value(std::list<ExecutionPath *> &checks, unsigned int varid, const std::string &value)
|
||||||
|
{
|
||||||
|
if (varid == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::list<ExecutionPath *>::const_iterator it;
|
||||||
|
for (it = checks.begin(); it != checks.end(); ++it)
|
||||||
|
{
|
||||||
|
ExecutionPathBufferOverrun *c = dynamic_cast<ExecutionPathBufferOverrun *>(*it);
|
||||||
|
if (c && c->varId == varid)
|
||||||
|
c->value = MathLib::toLongNumber(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Found array usage.. */
|
||||||
|
static void array_index(const Token *tok, std::list<ExecutionPath *> &checks, unsigned int varid1, unsigned int varid2)
|
||||||
|
{
|
||||||
|
if (varid1 == 0 || varid2 == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Locate array info corresponding to varid1
|
||||||
|
ArrayInfo ai;
|
||||||
|
{
|
||||||
|
ExecutionPathBufferOverrun *c = dynamic_cast<ExecutionPathBufferOverrun *>(checks.front());
|
||||||
|
std::map<unsigned int, ArrayInfo>::const_iterator it;
|
||||||
|
it = c->arrayInfo.find(varid1);
|
||||||
|
if (it == c->arrayInfo.end())
|
||||||
|
return;
|
||||||
|
ai = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if varid2 variable has a value that is out-of-bounds
|
||||||
|
std::list<ExecutionPath *>::const_iterator it;
|
||||||
|
for (it = checks.begin(); it != checks.end(); ++it)
|
||||||
|
{
|
||||||
|
ExecutionPathBufferOverrun *c = dynamic_cast<ExecutionPathBufferOverrun *>(*it);
|
||||||
|
if (c && c->varId == varid2 && c->value >= ai.num)
|
||||||
|
{
|
||||||
|
// variable value is out of bounds, report error
|
||||||
|
CheckBufferOverrun *checkBufferOverrun = dynamic_cast<CheckBufferOverrun *>(c->owner);
|
||||||
|
if (checkBufferOverrun)
|
||||||
|
{
|
||||||
|
checkBufferOverrun->arrayIndexOutOfBounds(tok, ai.num, c->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token *parse(const Token &tok, std::list<ExecutionPath *> &checks) const
|
||||||
|
{
|
||||||
|
if (Token::Match(tok.previous(), "[;{}]"))
|
||||||
|
{
|
||||||
|
// Declaring variable..
|
||||||
|
if (Token::Match(&tok, "%type% %var% ;") && tok.isStandardType())
|
||||||
|
{
|
||||||
|
checks.push_back(new ExecutionPathBufferOverrun(owner, arrayInfo, tok.next()->varId()));
|
||||||
|
return tok.tokAt(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign variable..
|
||||||
|
if (Token::Match(&tok, "%var% = %num% ;"))
|
||||||
|
{
|
||||||
|
assign_value(checks, tok.varId(), tok.strAt(2));
|
||||||
|
return tok.tokAt(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign variable (unknown value = 0)..
|
||||||
|
if (Token::Match(&tok, "%var% ="))
|
||||||
|
{
|
||||||
|
assign_value(checks, tok.varId(), "0");
|
||||||
|
return &tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array index..
|
||||||
|
if (Token::Match(&tok, "%var% [ %var% ]"))
|
||||||
|
{
|
||||||
|
array_index(&tok, checks, tok.varId(), tok.tokAt(2)->varId());
|
||||||
|
return tok.tokAt(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tok;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckBufferOverrun::executionPaths()
|
||||||
|
{
|
||||||
|
// Parse all tokens and extract array info..
|
||||||
|
std::map<unsigned int, ArrayInfo> arrayInfo;
|
||||||
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||||
|
{
|
||||||
|
if (Token::Match(tok, "[;{}] %type% %var% [ %num% ] ;"))
|
||||||
|
{
|
||||||
|
const unsigned int varid(tok->tokAt(2)->varId());
|
||||||
|
ArrayInfo ai;
|
||||||
|
ai.type_size = _tokenizer->sizeOfType(tok->next());
|
||||||
|
ai.num = MathLib::toLongNumber(tok->strAt(4));
|
||||||
|
arrayInfo[varid] = ai;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform checking - check how the arrayInfo arrays are used
|
||||||
|
ExecutionPathBufferOverrun c(this, arrayInfo);
|
||||||
|
checkExecutionPaths(_tokenizer->tokens(), &c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,17 @@ public:
|
||||||
{
|
{
|
||||||
CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger);
|
CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger);
|
||||||
checkBufferOverrun.bufferOverrun();
|
checkBufferOverrun.bufferOverrun();
|
||||||
|
|
||||||
|
/** ExecutionPath checking.. */
|
||||||
|
checkBufferOverrun.executionPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief %Check for buffer overruns */
|
/** @brief %Check for buffer overruns */
|
||||||
void bufferOverrun();
|
void bufferOverrun();
|
||||||
|
|
||||||
|
/** @brief %Check for buffer overruns by inspecting execution paths */
|
||||||
|
void executionPaths();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get minimum length of format string result
|
* @brief Get minimum length of format string result
|
||||||
* @param input_string format string
|
* @param input_string format string
|
||||||
|
@ -74,8 +80,6 @@ public:
|
||||||
*/
|
*/
|
||||||
static int countSprintfLength(const std::string &input_string, const std::list<const Token*> ¶meters);
|
static int countSprintfLength(const std::string &input_string, const std::list<const Token*> ¶meters);
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief %Check code that matches: "sprintf ( %varid% , %str% [,)]" when varid is not 0,
|
* @brief %Check code that matches: "sprintf ( %varid% , %str% [,)]" when varid is not 0,
|
||||||
* and report found errors.
|
* and report found errors.
|
||||||
|
|
|
@ -151,6 +151,8 @@ private:
|
||||||
TEST_CASE(terminateStrncpy1);
|
TEST_CASE(terminateStrncpy1);
|
||||||
TEST_CASE(terminateStrncpy2);
|
TEST_CASE(terminateStrncpy2);
|
||||||
TEST_CASE(recursive_long_time);
|
TEST_CASE(recursive_long_time);
|
||||||
|
|
||||||
|
TEST_CASE(executionPaths1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1918,6 +1920,39 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void epcheck(const char code[])
|
||||||
|
{
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
tokenizer.simplifyTokenList();
|
||||||
|
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Check for buffer overruns..
|
||||||
|
Settings settings;
|
||||||
|
CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this);
|
||||||
|
checkBufferOverrun.executionPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void executionPaths1()
|
||||||
|
{
|
||||||
|
epcheck("void f(int a)\n"
|
||||||
|
"{\n"
|
||||||
|
" int buf[10];\n"
|
||||||
|
" int i = 5;\n"
|
||||||
|
" if (a == 1)\n"
|
||||||
|
" i = 1000;\n"
|
||||||
|
" buf[i] = 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (error) Array 'buf[10]' index 1000 out of bounds\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestBufferOverrun)
|
REGISTER_TEST(TestBufferOverrun)
|
||||||
|
|
Loading…
Reference in New Issue