diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index a109e16cb..e13970054 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -337,6 +337,11 @@ void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) reportErr(tok, Severity::error, "memleak", "Memory leak: " + varname); } +void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &varname) +{ + reportErr(tok, Severity::error, "memleakOnRealloc", "Memory leak: \"" + varname + "\" nulled but not freed upon failure"); +} + void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) { std::string errmsg("Resource leak"); @@ -2103,6 +2108,29 @@ void CheckMemoryLeakInFunction::checkScope(const Token *Tok1, const std::string +//--------------------------------------------------------------------------- +// Check for memory leaks due to improper realloc() usage. +// Below, "a" may be set to null without being freed if realloc() cannot +// allocate the requested memory: +// a = malloc(10); a = realloc(a, 100); +//--------------------------------------------------------------------------- +void CheckMemoryLeakInFunction::checkReallocUsage() +{ + const Token *tok = _tokenizer->tokens(); + for (; tok; tok = tok->next()) + { + if (Token::Match(tok, "%var% = realloc|g_try_realloc ( %var% ,")) + { + if (tok->varId() == tok->tokAt(4)->varId()) + { + memleakUponReallocFailureError(tok, tok->str()); + } + } + } +} +//--------------------------------------------------------------------------- + + diff --git a/lib/checkmemoryleak.h b/lib/checkmemoryleak.h index bddd203f6..24d86963b 100644 --- a/lib/checkmemoryleak.h +++ b/lib/checkmemoryleak.h @@ -140,6 +140,7 @@ public: void deallocuseError(const Token *tok, const std::string &varname); void mismatchSizeError(const Token *tok, const std::string &sz); void mismatchAllocDealloc(const std::list &callstack, const std::string &varname); + void memleakUponReallocFailureError(const Token *tok, const std::string &varname); /** What type of allocated memory does the given function return? */ AllocType functionReturnType(const Token *tok) const; @@ -179,6 +180,7 @@ public: void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) { CheckMemoryLeakInFunction checkMemoryLeak(tokenizr, settings, errLog); + checkMemoryLeak.checkReallocUsage(); checkMemoryLeak.check(); } @@ -191,6 +193,11 @@ public: /** @brief experimental: checking via ExecutionPath */ void localleaks(); + /** + * Checking for a memory leak caused by improper realloc usage. + */ + void checkReallocUsage(); + /** * @brief %Check all variables in function scope * @param tok The first '{' token of the function body @@ -298,6 +305,7 @@ public: mismatchSizeError(0, "sz"); std::list callstack; mismatchAllocDealloc(callstack, "varname"); + memleakUponReallocFailureError(0, "varname"); } /** diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 6e3464f7d..96a4858de 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5187,6 +5187,15 @@ bool Tokenizer::simplifyKnownVariables() break; } + // Variable used in realloc (see Ticket #1649) + if (Token::Match(tok3, "%var% = realloc ( %var% ,") && + tok3->varId() == varid && + tok3->tokAt(4)->varId() == varid) + { + tok3->tokAt(4)->str(value); + ret = true; + } + // Variable is used somehow in a non-defined pattern => bail out if (tok3->varId() == varid) break; diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 3cd26fd02..f038f3620 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -216,7 +216,7 @@ private: settings.inconclusive = showAll; tokenizer.fillFunctionList(); CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this); - checkMemoryLeak.check(); + checkMemoryLeak.runSimplifiedChecks(&tokenizer, &settings, this); } @@ -1085,7 +1085,7 @@ private: " ;\n" " free(buf);\n" "}\n"); - TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: buf\n", errout.str()); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: \"buf\" nulled but not freed upon failure\n", errout.str()); } void if11() @@ -1174,7 +1174,7 @@ private: "\n" " return a;\n" "}\n", true); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: \"a\" nulled but not freed upon failure\n", errout.str()); } @@ -1195,7 +1195,8 @@ private: "\n" " return a;\n" "}\n", true); - ASSERT_EQUALS("[test.cpp:11]: (error) Memory leak: a\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: \"a\" nulled but not freed upon failure\n" + "[test.cpp:11]: (error) Memory leak: a\n", errout.str()); } @@ -1791,7 +1792,8 @@ private: " char *a = (char *)malloc(10);\n" " a = realloc(a, 100);\n" "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: \"a\" nulled but not freed upon failure\n" + "[test.cpp:5]: (error) Memory leak: a\n", errout.str()); } void realloc2() @@ -1803,7 +1805,7 @@ private: " free(a);\n" "}\n"); - TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: \"a\" nulled but not freed upon failure\n", errout.str()); } void realloc3()