Fixed #73 (memory leaks not found when calling a function that returns allocated memory through a parameter)
This commit is contained in:
parent
24a9bfc2c4
commit
4ac7da7d51
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue