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:
PKEuS 2015-01-30 20:27:48 +01:00
parent b69528eb80
commit 98e33a189f
5 changed files with 127 additions and 63 deletions

View File

@ -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;
}
}
} }
/** /**

View File

@ -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

View File

@ -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");

View File

@ -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"

View File

@ -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));