From f7ca4f09ef0874aecdbf590ce3bc5a78092db603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 12 Nov 2010 21:09:34 +0100 Subject: [PATCH] Fixed #2151 (false negative: memory leak when calling subfunction that doesn't deallocate the memory) --- lib/checkmemoryleak.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ lib/checkmemoryleak.h | 40 ++++++++++++++++++++++ test/testmemleak.cpp | 53 +++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index d12694d48..bfbdaf5e4 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -38,6 +38,7 @@ namespace CheckMemoryLeakInFunction instance1; CheckMemoryLeakInClass instance2; CheckMemoryLeakStructMember instance3; +CheckMemoryLeakNoVar instance4; } @@ -3215,3 +3216,77 @@ void CheckMemoryLeakInFunction::localleaks() checkExecutionPaths(_tokenizer->tokens(), &c); } + + +#include "checkuninitvar.h" // CheckUninitVar::analyse + +void CheckMemoryLeakNoVar::check() +{ + std::set 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."); +} + + diff --git a/lib/checkmemoryleak.h b/lib/checkmemoryleak.h index e384ae9b2..d77ed655a 100644 --- a/lib/checkmemoryleak.h +++ b/lib/checkmemoryleak.h @@ -434,6 +434,46 @@ private: 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 diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index aede99d05..4777f054e 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -3909,3 +3909,56 @@ private: 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;