Fixed #73 (memory leaks not found when calling a function that returns allocated memory through a parameter)

This commit is contained in:
Leandro Lisboa Penz 2010-06-04 20:58:50 -03:00
parent 24a9bfc2c4
commit 4ac7da7d51
3 changed files with 159 additions and 6 deletions

View File

@ -460,6 +460,80 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Token *tok)
}
CheckMemoryLeak::AllocType CheckMemoryLeak::functionArgAlloc(const Token *tok, unsigned int targetpar) const
{
// Find the varid of targetpar, then locate the start of the function..
unsigned int parlevel = 0;
unsigned int par = 0;
unsigned int varid = 0;
while (tok)
{
if (tok->str() == "{" || tok->str() == "}")
return No;
if (tok->str() == "(")
{
if (parlevel != 0)
return No;
++parlevel;
++par;
}
else if (tok->str() == ")")
{
if (parlevel != 1)
return No;
break;
}
else if (parlevel == 1 && tok->str() == ",")
{
++par;
}
tok = tok->next();
if (parlevel == 1 && par == targetpar && Token::Match(tok, "%type% * * %var%"))
{
varid = tok->tokAt(3)->varId();
}
}
if (varid == 0)
return No;
// Is this the start of a function?
if (!Token::Match(tok, ") const| {"))
return No;
while (tok->str() != "{")
tok = tok->next();
// Check if pointer is allocated.
AllocType allocType = No;
while (0 != (tok = tok->next()))
{
if (Token::Match(tok, "* %varid% =", varid))
{
allocType = getAllocationType(tok->tokAt(3), varid);
if (allocType == No)
{
allocType = getReallocationType(tok->tokAt(3), varid);
}
if (allocType != No)
{
return allocType;
}
}
if (tok->str() == "return")
return allocType;
}
return No;
}
void CheckMemoryLeakInFunction::parse_noreturn()
{
noreturn.insert("exit");
@ -558,7 +632,7 @@ bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname)
sizeof(call_func_white_list[0]), call_func_white_list_compare) != NULL);
}
const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, 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(tok->str()))
return 0;
@ -686,6 +760,21 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
Tokenizer::deleteTokens(func);
return ret;
}
if (varid > 0 && Token::Match(tok, "[,()] & %varid% [,()]", varid))
{
const Token *ftok = _tokenizer->getFunctionTokenByName(funcname.c_str());
AllocType a = functionArgAlloc(ftok, par);
if (a != No)
{
if (alloctype == No)
alloctype = a;
else if (alloctype != a)
alloctype = Many;
allocpar = true;
return "alloc";
}
}
}
}
return NULL;
@ -1225,18 +1314,23 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
continue;
}
const char *str = call_func(tok, callstack, varid, alloctype, dealloctype, sz);
bool allocpar = false;
const char *str = call_func(tok, callstack, varid, alloctype, dealloctype, allocpar, sz);
if (str)
{
if (varid == 0)
if (varid == 0 || str != std::string("alloc"))
{
addtoken(str);
}
else if (str != std::string("alloc") ||
Token::Match(tok->tokAt(-2), "%varid% =", varid))
else if (Token::Match(tok->tokAt(-2), "%varid% =", varid))
{
addtoken(str);
}
else if (allocpar)
{
addtoken(str);
tok = tok->next()->link();
}
}
else if (varid > 0 &&
getReallocationType(tok, varid) != No &&

View File

@ -144,6 +144,9 @@ public:
/** What type of allocated memory does the given function return? */
AllocType functionReturnType(const Token *tok) const;
/** Function allocates pointed-to argument (a la asprintf)? */
AllocType functionArgAlloc(const Token *tok, unsigned int parlevel0) const;
};
/// @}
@ -229,6 +232,7 @@ public:
* @param varid variable id to check
* @param alloctype if memory is allocated, this indicates the type of allocation
* @param dealloctype if memory is deallocated, this indicates the type of deallocation
* @param allocpar if function allocates varid parameter
* @param sz not used by call_func - see getcode
* @return These are the possible return values:
* - NULL : no significant code
@ -240,7 +244,7 @@ public:
* - "callfunc" : a function call with unknown side effects
* - "&use"
*/
const char * call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, unsigned int sz);
const char * call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz);
/**
* Extract a new tokens list that is easier to parse than the "_tokenizer->tokens()", the

View File

@ -297,6 +297,7 @@ private:
TEST_CASE(allocfunc2);
TEST_CASE(allocfunc3);
TEST_CASE(allocfunc4);
TEST_CASE(allocfunc5);
TEST_CASE(throw1);
TEST_CASE(throw2);
@ -1720,6 +1721,60 @@ private:
ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Mismatching allocation and deallocation: p\n"), errout.str());
}
void allocfunc5()
{
check("void foo(char **str)\n"
"{\n"
" *str = malloc(20);\n"
"}\n"
"\n"
"void bar()\n"
"{\n"
" char *p;\n"
" foo(&p);\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:10]: (error) Memory leak: p\n"), errout.str());
check("void foo(char **str)\n"
"{\n"
" *str = malloc(20);\n"
"}\n"
"\n"
"void bar()\n"
"{\n"
" char *p;\n"
" foo(&p);\n"
" delete p;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:10]: (error) Mismatching allocation and deallocation: p\n"), errout.str());
check("void foo(char **q, char **str)\n"
"{\n"
" *str = malloc(20);\n"
"}\n"
"\n"
"void bar()\n"
"{\n"
" char *p;\n"
" char *q;\n"
" foo(&q, &p);\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Memory leak: p\n"), errout.str());
check("void foo(char **str)\n"
"{\n"
" char *a = malloc(20)\n"
" *str = a;\n"
"}\n"
"\n"
"void bar()\n"
"{\n"
" char *p;\n"
" foo(&p);\n"
"}\n");
TODO_ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Memory leak: p\n"), errout.str());
}