Enhanced CheckBufferOverrun:
- Fixed bug in library: manual and existing libraries use "size", but library.cpp reads "sizeof" as podtype attribute - Fixed a couple of bugs in handling unknown size in checkbufferoverrun.cpp, get size from library if available.
This commit is contained in:
parent
b69528eb80
commit
98e33a189f
|
@ -652,11 +652,12 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
checkFunctionParameter(*tok, 2, arrayInfo, callstack);
|
checkFunctionParameter(*tok, 2, arrayInfo, callstack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (total_size > 0) {
|
||||||
// Writing data into array..
|
// Writing data into array..
|
||||||
if ((declarationId > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", declarationId)) ||
|
if (total_size > 0 && (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()))) {
|
||||||
const std::size_t len = Token::getStrLength(tok->tokAt(varcount + 4));
|
const std::size_t len = Token::getStrLength(tok->tokAt(varcount + 4));
|
||||||
if (total_size > 0 && len >= (unsigned int)total_size) {
|
if (len >= (unsigned int)total_size) {
|
||||||
bufferOverrunError(tok, declarationId > 0 ? emptyString : varnames);
|
bufferOverrunError(tok, declarationId > 0 ? emptyString : varnames);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -665,7 +666,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
const Variable *var = tok->tokAt(4)->variable();
|
const Variable *var = tok->tokAt(4)->variable();
|
||||||
if (var && var->isArray() && var->dimensions().size() == 1) {
|
if (var && var->isArray() && var->dimensions().size() == 1) {
|
||||||
const std::size_t len = (std::size_t)var->dimension(0);
|
const std::size_t len = (std::size_t)var->dimension(0);
|
||||||
if (total_size > 0 && len > (unsigned int)total_size) {
|
if (len > (unsigned int)total_size) {
|
||||||
if (_settings->inconclusive)
|
if (_settings->inconclusive)
|
||||||
possibleBufferOverrunError(tok, tok->strAt(4), tok->strAt(2), tok->str() == "strcat");
|
possibleBufferOverrunError(tok, tok->strAt(4), tok->strAt(2), tok->str() == "strcat");
|
||||||
continue;
|
continue;
|
||||||
|
@ -690,9 +691,8 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
}
|
}
|
||||||
|
|
||||||
// sprintf..
|
// sprintf..
|
||||||
// TODO: change total_size to an unsigned value and remove the "&& total_size > 0" check.
|
|
||||||
const std::string sprintfPattern = declarationId > 0 ? std::string("sprintf ( %varid% , %str% [,)]") : ("sprintf ( " + varnames + " , %str% [,)]");
|
const std::string sprintfPattern = declarationId > 0 ? std::string("sprintf ( %varid% , %str% [,)]") : ("sprintf ( " + varnames + " , %str% [,)]");
|
||||||
if (Token::Match(tok, sprintfPattern.c_str(), declarationId) && total_size > 0) {
|
if (Token::Match(tok, sprintfPattern.c_str(), declarationId)) {
|
||||||
checkSprintfCall(tok, static_cast<unsigned int>(total_size));
|
checkSprintfCall(tok, static_cast<unsigned int>(total_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,12 +700,12 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
const std::string snprintfPattern = declarationId > 0 ? std::string("snprintf ( %varid% , %num% ,") : ("snprintf ( " + varnames + " , %num% ,");
|
const std::string snprintfPattern = declarationId > 0 ? std::string("snprintf ( %varid% , %num% ,") : ("snprintf ( " + varnames + " , %num% ,");
|
||||||
if (Token::Match(tok, snprintfPattern.c_str(), declarationId)) {
|
if (Token::Match(tok, snprintfPattern.c_str(), declarationId)) {
|
||||||
const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4 + varcount));
|
const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4 + varcount));
|
||||||
if ((n > total_size) && total_size > 0)
|
if (n > total_size)
|
||||||
outOfBoundsError(tok->tokAt(4 + varcount), "snprintf size", true, n, total_size);
|
outOfBoundsError(tok->tokAt(4 + varcount), "snprintf size", true, n, total_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check function call..
|
// Check function call..
|
||||||
if (Token::Match(tok, "%var% (") && total_size > 0) {
|
if (Token::Match(tok, "%var% (")) {
|
||||||
// No varid => function calls are not handled
|
// No varid => function calls are not handled
|
||||||
if (declarationId == 0)
|
if (declarationId == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -714,9 +714,10 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
const std::list<const Token *> callstack;
|
const std::list<const Token *> callstack;
|
||||||
checkFunctionCall(tok, arrayInfo1, callstack);
|
checkFunctionCall(tok, arrayInfo1, callstack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// undefined behaviour: result of pointer arithmetic is out of bounds
|
// undefined behaviour: result of pointer arithmetic is out of bounds
|
||||||
else if (declarationId && Token::Match(tok, "= %varid% + %num% ;", declarationId)) {
|
if (declarationId && Token::Match(tok, "= %varid% + %num% ;", declarationId)) {
|
||||||
const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3));
|
const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3));
|
||||||
if (isPortabilityEnabled && index > size)
|
if (isPortabilityEnabled && index > size)
|
||||||
pointerOutOfBoundsError(tok->tokAt(2));
|
pointerOutOfBoundsError(tok->tokAt(2));
|
||||||
|
@ -1093,7 +1094,7 @@ void CheckBufferOverrun::checkGlobalAndLocalVariable()
|
||||||
break;
|
break;
|
||||||
if (tok->str() == "{")
|
if (tok->str() == "{")
|
||||||
tok = tok->next();
|
tok = tok->next();
|
||||||
const ArrayInfo arrayInfo(var, _tokenizer, i);
|
const ArrayInfo arrayInfo(var, _tokenizer, &_settings->library, i);
|
||||||
checkScope(tok, arrayInfo);
|
checkScope(tok, arrayInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1212,7 @@ void CheckBufferOverrun::checkStructVariable()
|
||||||
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) {
|
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) {
|
||||||
if (var->isArray()) {
|
if (var->isArray()) {
|
||||||
// create ArrayInfo from the array variable
|
// create ArrayInfo from the array variable
|
||||||
ArrayInfo arrayInfo(&*var, _tokenizer);
|
ArrayInfo arrayInfo(&*var, _tokenizer, &_settings->library);
|
||||||
|
|
||||||
// find every function
|
// find every function
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
|
@ -1416,7 +1417,7 @@ void CheckBufferOverrun::bufferOverrun2()
|
||||||
if (var->dimension(0) <= 1 && Token::simpleMatch(var->nameToken()->linkAt(1),"] ; }"))
|
if (var->dimension(0) <= 1 && Token::simpleMatch(var->nameToken()->linkAt(1),"] ; }"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ArrayInfo arrayInfo(var,_tokenizer);
|
ArrayInfo arrayInfo(var, _tokenizer, &_settings->library);
|
||||||
arrayInfo.varname(varname);
|
arrayInfo.varname(varname);
|
||||||
|
|
||||||
valueFlowCheckArrayIndex(tok->next(), arrayInfo);
|
valueFlowCheckArrayIndex(tok->next(), arrayInfo);
|
||||||
|
@ -1728,7 +1729,7 @@ CheckBufferOverrun::ArrayInfo::ArrayInfo()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const Tokenizer *tokenizer, const unsigned int forcedeclid)
|
CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const Tokenizer *tokenizer, const Library *library, const unsigned int forcedeclid)
|
||||||
: _varname(var->name()), _declarationId((forcedeclid == 0U) ? var->declarationId() : forcedeclid)
|
: _varname(var->name()), _declarationId((forcedeclid == 0U) ? var->declarationId() : forcedeclid)
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < var->dimensions().size(); i++)
|
for (std::size_t i = 0; i < var->dimensions().size(); i++)
|
||||||
|
@ -1737,8 +1738,14 @@ CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const Tokenizer *t
|
||||||
_element_size = tokenizer->sizeOfType(var->typeEndToken());
|
_element_size = tokenizer->sizeOfType(var->typeEndToken());
|
||||||
else if (var->typeStartToken()->str() == "struct")
|
else if (var->typeStartToken()->str() == "struct")
|
||||||
_element_size = 100;
|
_element_size = 100;
|
||||||
else
|
else {
|
||||||
_element_size = tokenizer->sizeOfType(var->typeEndToken());
|
_element_size = tokenizer->sizeOfType(var->typeEndToken());
|
||||||
|
if (!_element_size) { // try libary
|
||||||
|
const Library::PodType* podtype = library->podtype(var->typeEndToken()->str());
|
||||||
|
if (podtype)
|
||||||
|
_element_size = podtype->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -126,7 +126,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ArrayInfo();
|
ArrayInfo();
|
||||||
ArrayInfo(const Variable *var, const Tokenizer *tokenizer, const unsigned int forcedeclid = 0);
|
ArrayInfo(const Variable *var, const Tokenizer *tokenizer, const Library *library, const unsigned int forcedeclid = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create array info with specified data
|
* Create array info with specified data
|
||||||
|
|
|
@ -503,7 +503,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
||||||
if (!name)
|
if (!name)
|
||||||
return Error(MISSING_ATTRIBUTE, "name");
|
return Error(MISSING_ATTRIBUTE, "name");
|
||||||
PodType podType = {0};
|
PodType podType = {0};
|
||||||
const char * const size = node->Attribute("sizeof");
|
const char * const size = node->Attribute("size");
|
||||||
if (size)
|
if (size)
|
||||||
podType.size = atoi(size);
|
podType.size = atoi(size);
|
||||||
const char * const sign = node->Attribute("sign");
|
const char * const sign = node->Attribute("sign");
|
||||||
|
|
|
@ -82,6 +82,7 @@ private:
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
std::istringstream istr(code);
|
std::istringstream istr(code);
|
||||||
tokenizer.tokenize(istr, filename);
|
tokenizer.tokenize(istr, filename);
|
||||||
|
tokenizer.simplifyTokenList2();
|
||||||
|
|
||||||
// Clear the error buffer..
|
// Clear the error buffer..
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
@ -223,6 +224,7 @@ private:
|
||||||
TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch
|
TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch
|
||||||
TEST_CASE(buffer_overrun_function_array_argument);
|
TEST_CASE(buffer_overrun_function_array_argument);
|
||||||
TEST_CASE(possible_buffer_overrun_1); // #3035
|
TEST_CASE(possible_buffer_overrun_1); // #3035
|
||||||
|
TEST_CASE(buffer_overrun_readSizeFromCfg);
|
||||||
|
|
||||||
TEST_CASE(valueflow_string); // using ValueFlow string values in checking
|
TEST_CASE(valueflow_string); // using ValueFlow string values in checking
|
||||||
|
|
||||||
|
@ -2733,7 +2735,7 @@ private:
|
||||||
" char* const source = (char*) malloc(sizeof(dest));\n"
|
" char* const source = (char*) malloc(sizeof(dest));\n"
|
||||||
" memcpy(&dest, source + sizeof(double), sizeof(dest));\n"
|
" memcpy(&dest, source + sizeof(double), sizeof(dest));\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str());
|
||||||
|
|
||||||
checkstd("void foo() {\n"
|
checkstd("void foo() {\n"
|
||||||
" double dest = 23.0;\n"
|
" double dest = 23.0;\n"
|
||||||
|
@ -2932,6 +2934,61 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void buffer_overrun_readSizeFromCfg() {
|
||||||
|
// Attempt to get size from Cfg files, no false positives if size is not specified
|
||||||
|
checkstd("void f() {\n"
|
||||||
|
" uint8_t str[256];\n"
|
||||||
|
" str[0] = 0;\n"
|
||||||
|
" strcat(str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkstd("void f() {\n"
|
||||||
|
" uint8_t str[2];\n"
|
||||||
|
" str[0] = 0;\n"
|
||||||
|
" strcat(str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
|
||||||
|
|
||||||
|
checkstd("void f() {\n"
|
||||||
|
" int_fast8_t str[256];\n"
|
||||||
|
" str[0] = 0;\n"
|
||||||
|
" strcat(str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// The same for structs, where the message comes from a different check
|
||||||
|
checkstd("typedef struct mystruct_s {\n"
|
||||||
|
" uint8_t str[256];\n"
|
||||||
|
"} mystruct_t;\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" mystruct_t ms;\n"
|
||||||
|
" ms.str[0] = 0;\n"
|
||||||
|
" strcat((char*)ms.str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkstd("typedef struct mystruct_s {\n"
|
||||||
|
" uint8_t str[2];\n"
|
||||||
|
"} mystruct_t;\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" mystruct_t ms;\n"
|
||||||
|
" ms.str[0] = 0;\n"
|
||||||
|
" strcat((char*)ms.str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: ms.str\n", errout.str());
|
||||||
|
|
||||||
|
checkstd("typedef struct mystruct_s {\n"
|
||||||
|
" int_fast8_t str[256];\n"
|
||||||
|
"} mystruct_t;\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" mystruct_t ms;\n"
|
||||||
|
" ms.str[0] = 0;\n"
|
||||||
|
" strcat((char*)ms.str, \"toto\");\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void valueflow_string() { // using ValueFlow string values in checking
|
void valueflow_string() { // using ValueFlow string values in checking
|
||||||
checkstd("void f() {\n"
|
checkstd("void f() {\n"
|
||||||
" char buf[3];\n"
|
" char buf[3];\n"
|
||||||
|
|
|
@ -301,7 +301,7 @@ private:
|
||||||
void podtype() const {
|
void podtype() const {
|
||||||
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
||||||
"<def>\n"
|
"<def>\n"
|
||||||
" <podtype name=\"s16\" sizeof=\"2\"/>\n"
|
" <podtype name=\"s16\" size=\"2\"/>\n"
|
||||||
"</def>";
|
"</def>";
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
doc.Parse(xmldata, sizeof(xmldata));
|
doc.Parse(xmldata, sizeof(xmldata));
|
||||||
|
|
Loading…
Reference in New Issue