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()
|
void CheckMemoryLeakInFunction::parse_noreturn()
|
||||||
{
|
{
|
||||||
noreturn.insert("exit");
|
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);
|
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()))
|
if (test_white_list(tok->str()))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -686,6 +760,21 @@ const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list<co
|
||||||
Tokenizer::deleteTokens(func);
|
Tokenizer::deleteTokens(func);
|
||||||
return ret;
|
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;
|
return NULL;
|
||||||
|
@ -1225,18 +1314,23 @@ Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list<const Toke
|
||||||
continue;
|
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 (str)
|
||||||
{
|
{
|
||||||
if (varid == 0)
|
if (varid == 0 || str != std::string("alloc"))
|
||||||
{
|
{
|
||||||
addtoken(str);
|
addtoken(str);
|
||||||
}
|
}
|
||||||
else if (str != std::string("alloc") ||
|
else if (Token::Match(tok->tokAt(-2), "%varid% =", varid))
|
||||||
Token::Match(tok->tokAt(-2), "%varid% =", varid))
|
|
||||||
{
|
{
|
||||||
addtoken(str);
|
addtoken(str);
|
||||||
}
|
}
|
||||||
|
else if (allocpar)
|
||||||
|
{
|
||||||
|
addtoken(str);
|
||||||
|
tok = tok->next()->link();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (varid > 0 &&
|
else if (varid > 0 &&
|
||||||
getReallocationType(tok, varid) != No &&
|
getReallocationType(tok, varid) != No &&
|
||||||
|
|
|
@ -144,6 +144,9 @@ public:
|
||||||
|
|
||||||
/** What type of allocated memory does the given function return? */
|
/** What type of allocated memory does the given function return? */
|
||||||
AllocType functionReturnType(const Token *tok) const;
|
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 varid variable id to check
|
||||||
* @param alloctype if memory is allocated, this indicates the type of allocation
|
* @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 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
|
* @param sz not used by call_func - see getcode
|
||||||
* @return These are the possible return values:
|
* @return These are the possible return values:
|
||||||
* - NULL : no significant code
|
* - NULL : no significant code
|
||||||
|
@ -240,7 +244,7 @@ public:
|
||||||
* - "callfunc" : a function call with unknown side effects
|
* - "callfunc" : a function call with unknown side effects
|
||||||
* - "&use"
|
* - "&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
|
* 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(allocfunc2);
|
||||||
TEST_CASE(allocfunc3);
|
TEST_CASE(allocfunc3);
|
||||||
TEST_CASE(allocfunc4);
|
TEST_CASE(allocfunc4);
|
||||||
|
TEST_CASE(allocfunc5);
|
||||||
|
|
||||||
TEST_CASE(throw1);
|
TEST_CASE(throw1);
|
||||||
TEST_CASE(throw2);
|
TEST_CASE(throw2);
|
||||||
|
@ -1720,6 +1721,60 @@ private:
|
||||||
ASSERT_EQUALS(std::string("[test.cpp:11]: (error) Mismatching allocation and deallocation: p\n"), errout.str());
|
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