CheckMemoryLeakInFunction: Don't treat delete as delete operator for C code

Fixed GCC message in checkbufferoverrun.cpp
This commit is contained in:
PKEuS 2015-01-30 20:55:53 +01:00
parent 98e33a189f
commit 03e44d4aa0
4 changed files with 75 additions and 61 deletions

View File

@ -654,7 +654,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
if (total_size > 0) { if (total_size > 0) {
// Writing data into array.. // Writing data into array..
if (total_size > 0 && (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()))) {
const std::size_t len = Token::getStrLength(tok->tokAt(varcount + 4)); const std::size_t len = Token::getStrLength(tok->tokAt(varcount + 4));
if (len >= (unsigned int)total_size) { if (len >= (unsigned int)total_size) {

View File

@ -64,7 +64,7 @@ static unsigned int countParameters(const Token *tok)
static const char * const call_func_white_list[] = { static const char * const call_func_white_list[] = {
"_open", "_wopen", "access", "adjtime", "asctime", "asctime_r", "asprintf", "assert" "_open", "_wopen", "access", "adjtime", "asctime", "asctime_r", "asprintf", "assert"
, "atof", "atoi", "atol", "chdir", "chmod", "chown" , "atof", "atoi", "atol", "chdir", "chmod", "chown"
, "clearerr", "creat", "ctime", "ctime_r", "delete", "execl", "execle" , "clearerr", "creat", "ctime", "ctime_r", "execl", "execle"
, "execlp", "execv", "execve", "fchmod", "fclose", "fcntl" , "execlp", "execv", "execve", "fchmod", "fclose", "fcntl"
, "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets" , "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets"
, "flock", "fmemopen", "fnmatch", "fopen", "fopencookie", "for", "fprintf", "fputc", "fputs", "fread", "free" , "flock", "fmemopen", "fnmatch", "fopen", "fopencookie", "for", "fprintf", "fputc", "fputs", "fread", "free"
@ -239,17 +239,19 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok
CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, unsigned int varid) const CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, unsigned int varid) const
{ {
if (Token::Match(tok, "delete %varid% ;", varid)) if (tokenizer->isCPP()) {
return New; if (Token::Match(tok, "delete %varid% ;", varid))
return New;
if (Token::Match(tok, "delete [ ] %varid% ;", varid)) if (Token::Match(tok, "delete [ ] %varid% ;", varid))
return NewArray; return NewArray;
if (Token::Match(tok, "delete ( %varid% ) ;", varid)) if (Token::Match(tok, "delete ( %varid% ) ;", varid))
return New; return New;
if (Token::Match(tok, "delete [ ] ( %varid% ) ;", varid)) if (Token::Match(tok, "delete [ ] ( %varid% ) ;", varid))
return NewArray; return NewArray;
}
if (tok && tok->str() == "::") if (tok && tok->str() == "::")
tok = tok->next(); tok = tok->next();
@ -283,17 +285,19 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok
CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, const std::string &varname) const CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, const std::string &varname) const
{ {
if (Token::Match(tok, std::string("delete " + varname + " [,;]").c_str())) if (tokenizer->isCPP()) {
return New; if (Token::Match(tok, std::string("delete " + varname + " [,;]").c_str()))
return New;
if (Token::Match(tok, std::string("delete [ ] " + varname + " [,;]").c_str())) if (Token::Match(tok, std::string("delete [ ] " + varname + " [,;]").c_str()))
return NewArray; return NewArray;
if (Token::Match(tok, std::string("delete ( " + varname + " ) [,;]").c_str())) if (Token::Match(tok, std::string("delete ( " + varname + " ) [,;]").c_str()))
return New; return New;
if (Token::Match(tok, std::string("delete [ ] ( " + varname + " ) [,;]").c_str())) if (Token::Match(tok, std::string("delete [ ] ( " + varname + " ) [,;]").c_str()))
return NewArray; return NewArray;
}
if (Token::simpleMatch(tok, std::string("free ( " + varname + " ) ;").c_str()) || if (Token::simpleMatch(tok, std::string("free ( " + varname + " ) ;").c_str()) ||
Token::simpleMatch(tok, std::string("kfree ( " + varname + " ) ;").c_str()) || Token::simpleMatch(tok, std::string("kfree ( " + varname + " ) ;").c_str()) ||
@ -523,25 +527,17 @@ bool CheckMemoryLeakInFunction::notvar(const Token *tok, unsigned int varid, boo
bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname) bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname, const Settings *settings, bool cpp)
{ {
return (std::binary_search(call_func_white_list, return (std::binary_search(call_func_white_list,
call_func_white_list+sizeof(call_func_white_list) / sizeof(call_func_white_list[0]), call_func_white_list+sizeof(call_func_white_list) / sizeof(call_func_white_list[0]),
funcname)); funcname) || (settings->library.leakignore.find(funcname) != settings->library.leakignore.end()) || (cpp && funcname == "delete"));
}
bool CheckMemoryLeakInFunction::test_white_list_with_lib(const std::string &funcname, const Settings *settings)
{
return (std::binary_search(call_func_white_list,
call_func_white_list+sizeof(call_func_white_list) / sizeof(call_func_white_list[0]),
funcname) || (settings->library.leakignore.find(funcname) != settings->library.leakignore.end()));
} }
const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz) const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz)
{ {
if (test_white_list_with_lib(tok->str(), _settings)) { if (test_white_list(tok->str(), _settings, tokenizer->isCPP())) {
if (tok->str() == "asprintf" || if (tok->str() == "asprintf" ||
tok->str() == "delete" || tok->str() == "delete" ||
tok->str() == "fclose" || tok->str() == "fclose" ||
@ -1024,7 +1020,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
dep = true; dep = true;
} else if (Token::Match(tok2, "! %varid%", varid)) { } else if (Token::Match(tok2, "! %varid%", varid)) {
dep = true; dep = true;
} else if (Token::Match(tok2, "%var% (") && !test_white_list_with_lib(tok2->str(), _settings)) { } else if (Token::Match(tok2, "%var% (") && !test_white_list(tok2->str(), _settings, tokenizer->isCPP())) {
bool use = false; bool use = false;
for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->nextArgument()) { for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->nextArgument()) {
if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) { if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) {
@ -1200,7 +1196,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
if (!Token::Match(tok2->previous(), "&|(") && if (!Token::Match(tok2->previous(), "&|(") &&
tok2->strAt(1) == "[") { tok2->strAt(1) == "[") {
} else if (f.empty() || } else if (f.empty() ||
!test_white_list_with_lib(f.top()->str(), _settings) || !test_white_list(f.top()->str(), _settings, tokenizer->isCPP()) ||
getDeallocationType(f.top(),varid)) { getDeallocationType(f.top(),varid)) {
use = true; use = true;
} }
@ -1286,7 +1282,7 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
if (_settings->library.isnoreturn(tok)) if (_settings->library.isnoreturn(tok))
addtoken(&rettail, tok, "exit"); addtoken(&rettail, tok, "exit");
else if (!test_white_list_with_lib(tok->str(), _settings)) { else if (!test_white_list(tok->str(), _settings, tokenizer->isCPP())) {
const Token* const end2 = tok->linkAt(1); const Token* const end2 = tok->linkAt(1);
for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
if (tok2->varId() == varid) { if (tok2->varId() == varid) {
@ -2402,7 +2398,7 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam
// Function call .. possible deallocation // Function call .. possible deallocation
else if (Token::Match(tok->previous(), "[{};] %var% (")) { else if (Token::Match(tok->previous(), "[{};] %var% (")) {
if (!CheckMemoryLeakInFunction::test_white_list_with_lib(tok->str(), _settings)) { if (!CheckMemoryLeakInFunction::test_white_list(tok->str(), _settings, tokenizer->isCPP())) {
return; return;
} }
} }
@ -2717,12 +2713,12 @@ void CheckMemoryLeakNoVar::check()
// Is it a function call.. // Is it a function call..
if (!Token::Match(tok3->tokAt(-2), "= %var% (")) { if (!Token::Match(tok3->tokAt(-2), "= %var% (")) {
const std::string& functionName = tok3->strAt(-1); const std::string& functionName = tok3->strAt(-1);
if (functionName == "delete" || if ((tokenizer->isCPP() && functionName == "delete") ||
functionName == "free" || functionName == "free" ||
functionName == "fclose" || functionName == "fclose" ||
functionName == "realloc") functionName == "realloc")
break; break;
if (CheckMemoryLeakInFunction::test_white_list_with_lib(functionName, _settings)) { if (CheckMemoryLeakInFunction::test_white_list(functionName, _settings, tokenizer->isCPP())) {
functionCallLeak(tok2, tok2->strAt(1), functionName); functionCallLeak(tok2, tok2->strAt(1), functionName);
break; break;
} }

View File

@ -47,10 +47,11 @@ class Variable;
/** @brief Base class for memory leaks checking */ /** @brief Base class for memory leaks checking */
class CPPCHECKLIB CheckMemoryLeak { class CPPCHECKLIB CheckMemoryLeak {
private: protected:
/** For access to the tokens */ /** For access to the tokens */
const Tokenizer * const tokenizer; const Tokenizer * const tokenizer;
private:
/** ErrorLogger used to report errors */ /** ErrorLogger used to report errors */
ErrorLogger * const errorLogger; ErrorLogger * const errorLogger;
@ -202,8 +203,7 @@ public:
} }
/** @brief Unit testing : testing the white list */ /** @brief Unit testing : testing the white list */
static bool test_white_list(const std::string &funcname); static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp);
static bool test_white_list_with_lib(const std::string &funcname, const Settings *settings);
/** @brief Perform checking */ /** @brief Perform checking */
void check(); void check();

View File

@ -128,7 +128,7 @@ private:
Settings settings1; Settings settings1;
Settings settings2; Settings settings2;
void check(const char code[], const Settings *settings = nullptr) { void check(const char code[], const Settings *settings = nullptr, bool c = false) {
// Clear the error buffer.. // Clear the error buffer..
errout.str(""); errout.str("");
@ -138,7 +138,7 @@ private:
// Tokenize.. // Tokenize..
Tokenizer tokenizer(settings, this); Tokenizer tokenizer(settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp"); tokenizer.tokenize(istr, c?"test.c":"test.cpp");
tokenizer.simplifyTokenList2(); tokenizer.simplifyTokenList2();
// Check for memory leaks.. // Check for memory leaks..
@ -365,6 +365,8 @@ private:
TEST_CASE(ptrptr); TEST_CASE(ptrptr);
TEST_CASE(c_code);
// test that the cfg files are configured correctly // test that the cfg files are configured correctly
TEST_CASE(posixcfg); TEST_CASE(posixcfg);
TEST_CASE(posixcfg_mmap); TEST_CASE(posixcfg_mmap);
@ -594,33 +596,39 @@ private:
ASSERT_EQUALS(";;catch{}", getcode("char *s; catch(err) { }", "s")); ASSERT_EQUALS(";;catch{}", getcode("char *s; catch(err) { }", "s"));
} }
bool test_white_list(const std::string& str, bool cpp = true) const {
return CheckMemoryLeakInFunction::test_white_list(str, &settings1, cpp);
}
void call_func() const { void call_func() const {
// whitelist.. // whitelist..
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("qsort")); ASSERT_EQUALS(true, test_white_list("qsort"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("scanf")); ASSERT_EQUALS(true, test_white_list("scanf"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("sscanf")); ASSERT_EQUALS(true, test_white_list("sscanf"));
// #1293 // #1293
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("time")); ASSERT_EQUALS(true, test_white_list("time"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("asctime")); ASSERT_EQUALS(true, test_white_list("asctime"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("asctime_r")); ASSERT_EQUALS(true, test_white_list("asctime_r"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("ctime")); ASSERT_EQUALS(true, test_white_list("ctime"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("ctime_r")); ASSERT_EQUALS(true, test_white_list("ctime_r"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gmtime")); ASSERT_EQUALS(true, test_white_list("gmtime"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gmtime_r")); ASSERT_EQUALS(true, test_white_list("gmtime_r"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("localtime")); ASSERT_EQUALS(true, test_white_list("localtime"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("localtime_r")); ASSERT_EQUALS(true, test_white_list("localtime_r"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("memcmp")); ASSERT_EQUALS(true, test_white_list("memcmp"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("gets")); ASSERT_EQUALS(true, test_white_list("gets"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vprintf")); ASSERT_EQUALS(true, test_white_list("vprintf"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vfprintf")); ASSERT_EQUALS(true, test_white_list("vfprintf"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vsprintf")); ASSERT_EQUALS(true, test_white_list("vsprintf"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("snprintf")); ASSERT_EQUALS(true, test_white_list("snprintf"));
ASSERT_EQUALS(true, CheckMemoryLeakInFunction::test_white_list("vsnprintf")); ASSERT_EQUALS(true, test_white_list("vsnprintf"));
ASSERT_EQUALS(true, test_white_list("delete", true));
ASSERT_EQUALS(false, test_white_list("delete", false));
static const char * const call_func_white_list[] = { static const char * const call_func_white_list[] = {
"access", "asprintf", "atof", "atoi", "atol", "chdir", "chmod", "clearerr", "chown", "delete" "access", "asprintf", "atof", "atoi", "atol", "chdir", "chmod", "clearerr", "chown"
, "fchmod", "fcntl", "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets" , "fchmod", "fcntl", "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets"
, "flock", "for", "fprintf", "fputc", "fputs", "fread", "free", "freopen", "fscanf", "fseek" , "flock", "for", "fprintf", "fputc", "fputs", "fread", "free", "freopen", "fscanf", "fseek"
, "fseeko", "fsetpos", "fstat", "fsync", "ftell", "ftello", "ftruncate" , "fseeko", "fsetpos", "fstat", "fsync", "ftell", "ftello", "ftruncate"
@ -644,7 +652,7 @@ private:
}; };
for (unsigned int i = 0; i < (sizeof(call_func_white_list) / sizeof(char *)); ++i) { for (unsigned int i = 0; i < (sizeof(call_func_white_list) / sizeof(char *)); ++i) {
bool ret = CheckMemoryLeakInFunction::test_white_list(call_func_white_list[i]); bool ret = test_white_list(call_func_white_list[i]);
ASSERT_EQUALS("", ret ? "" : call_func_white_list[i]); ASSERT_EQUALS("", ret ? "" : call_func_white_list[i]);
} }
} }
@ -4262,6 +4270,16 @@ private:
ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: p\n", errout.str());
} }
void c_code() {
check("int main(void) {\n"
" struct llist *ll = malloc(sizeof(struct llist));\n"
" free(ll);\n"
" ll = NULL;\n"
" delete(ll, ll->top);\n"
"}", nullptr, true);
ASSERT_EQUALS("", errout.str());
}
void gnucfg() { void gnucfg() {
Settings settings; Settings settings;
settings.standards.posix = true; settings.standards.posix = true;