Fixed #2151 (false negative: memory leak when calling subfunction that doesn't deallocate the memory)

This commit is contained in:
Daniel Marjamäki 2010-11-12 21:09:34 +01:00
parent a8d7ac0f0d
commit f7ca4f09ef
3 changed files with 168 additions and 0 deletions

View File

@ -38,6 +38,7 @@ namespace
CheckMemoryLeakInFunction instance1; CheckMemoryLeakInFunction instance1;
CheckMemoryLeakInClass instance2; CheckMemoryLeakInClass instance2;
CheckMemoryLeakStructMember instance3; CheckMemoryLeakStructMember instance3;
CheckMemoryLeakNoVar instance4;
} }
@ -3215,3 +3216,77 @@ void CheckMemoryLeakInFunction::localleaks()
checkExecutionPaths(_tokenizer->tokens(), &c); checkExecutionPaths(_tokenizer->tokens(), &c);
} }
#include "checkuninitvar.h" // CheckUninitVar::analyse
void CheckMemoryLeakNoVar::check()
{
std::set<std::string> uvarFunctions;
{
const CheckUninitVar c(_tokenizer, _settings, _errorLogger);
c.analyse(_tokenizer->tokens(), uvarFunctions);
}
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
// Search for executable scopes..
if (!Token::Match(tok, ") const| {"))
continue;
// goto the "{"
tok = tok->next();
if (tok->str() != "{")
tok = tok->next();
// goto the "}" that ends the executable scope..
tok = tok->link();
// parse the executable scope until tok is reached...
for (const Token *tok2 = tok->link(); tok2 && tok2 != tok; tok2 = tok2->next())
{
// allocating memory in parameter for function call..
if (Token::Match(tok2, "[(,] %var% (") && Token::Match(tok2->tokAt(2)->link(), ") [,)]"))
{
const AllocType allocType = getAllocationType(tok2->next(), 0);
if (allocType != No)
{
// locate outer function call..
for (const Token *tok3 = tok2; tok3; tok3 = tok3->previous())
{
if (tok3->str() == "(")
{
// Is it a function call..
if (Token::Match(tok3->tokAt(-2), "[(,;{}] %var% ("))
{
const std::string functionName = tok3->strAt(-1);
if (CheckMemoryLeakInFunction::test_white_list(functionName))
{
functionCallLeak(tok2, tok2->strAt(1), functionName);
break;
}
if (uvarFunctions.find(functionName) != uvarFunctions.end())
{
functionCallLeak(tok2, tok2->strAt(1), functionName);
break;
}
}
break;
}
else if (tok3->str() == ")")
tok3 = tok3->link();
else if (Token::Match(tok3, "[;{}]"))
break;
}
}
}
}
}
}
void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall)
{
reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.");
}

View File

@ -434,6 +434,46 @@ private:
return "Don't forget to deallocate struct members"; return "Don't forget to deallocate struct members";
} }
}; };
/** @brief detect simple memory leaks (address not taken) */
class CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak
{
public:
CheckMemoryLeakNoVar() : Check(), CheckMemoryLeak(0, 0)
{ }
CheckMemoryLeakNoVar(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
: Check(tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog)
{ }
void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
{
CheckMemoryLeakNoVar checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.check();
}
void check();
private:
void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
void getErrorMessages()
{ }
std::string name() const
{
return "Memory leaks (address not taken)";
}
std::string classInfo() const
{
return "Not taking the address to allocated memory";
}
};
/// @} /// @}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#endif #endif

View File

@ -3909,3 +3909,56 @@ private:
static TestMemleakStructMember testMemleakStructMember; static TestMemleakStructMember testMemleakStructMember;
class TestMemleakNoVar : public TestFixture
{
public:
TestMemleakNoVar() : TestFixture("TestMemleakNoVar")
{ }
private:
void check(const char code[])
{
// Tokenize..
Tokenizer tokenizer;
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.simplifyTokenList();
// Clear the error buffer..
errout.str("");
// Check for memory leaks..
Settings settings;
CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this);
checkMemoryLeakNoVar.check();
}
void run()
{
// pass allocated memory to function..
TEST_CASE(functionParameter);
}
void functionParameter()
{
// standard function..
check("void x() {\n"
" strcpy(a, strdup(p));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str());
// user function..
check("void set_error(const char *msg) {\n"
"}\n"
"\n"
"void x() {\n"
" set_error(strdup(p));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", errout.str());
}
};
static TestMemleakNoVar testMemleakNoVar;