diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index f2f4e1a80..8bede0c08 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -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 callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, unsigned int sz) +const char * CheckMemoryLeakInFunction::call_func(const Token *tok, std::list 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 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::listtokAt(-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 && diff --git a/lib/checkmemoryleak.h b/lib/checkmemoryleak.h index 24d86963b..b5a31c55b 100644 --- a/lib/checkmemoryleak.h +++ b/lib/checkmemoryleak.h @@ -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 callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, unsigned int sz); + const char * call_func(const Token *tok, std::list 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 diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 2269f60ee..345197c18 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -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()); + } +