diff --git a/cfg/windows.cfg b/cfg/windows.cfg index 55f91676d..7cffeb7d9 100644 --- a/cfg/windows.cfg +++ b/cfg/windows.cfg @@ -1001,7 +1001,7 @@ HeapAlloc HeapReAlloc - HeapFree + HeapFree IoAllocateErrorLogEntry @@ -1062,7 +1062,7 @@ VirtualAllocEx VirtualAllocExNuma - VirtualFreeEx + VirtualFreeEx LocalAlloc diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 3f1e48fb4..f167afa5d 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -207,7 +207,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, if (tok->str() == "(" && tok->previous()->isName()) { VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); - functionCall(tok->previous(), varInfo, allocation); + functionCall(tok->previous(), varInfo, allocation, nullptr); tok = tok->link(); continue; } @@ -274,9 +274,9 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, // allocation? if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) { - int i = _settings->library.alloc(varTok->next()->astOperand2()->previous()); - if (i > 0) { - alloctype[varTok->varId()].type = i; + const Library::AllocFunc* f = _settings->library.alloc(varTok->next()->astOperand2()->previous()); + if (f && f->arg == -1) { + alloctype[varTok->varId()].type = f->groupId; alloctype[varTok->varId()].status = VarInfo::ALLOC; } } else if (_tokenizer->isCPP() && varTok->strAt(2) == "new") { @@ -301,10 +301,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, if (innerTok->str() == ")") break; if (innerTok->str() == "(" && innerTok->previous()->isName()) { - VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC); - if (allocation.type == 0) - allocation.status = VarInfo::NOALLOC; - functionCall(innerTok->previous(), varInfo, allocation); + VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); + functionCall(innerTok->previous(), varInfo, allocation, nullptr); innerTok = innerTok->link(); } } @@ -440,10 +438,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, // Function call.. else if (Token::Match(tok, "%type% (")) { - VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC); + const Library::AllocFunc* af = _settings->library.dealloc(tok); + VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC); if (allocation.type == 0) allocation.status = VarInfo::NOALLOC; - functionCall(tok, varInfo, allocation); + functionCall(tok, varInfo, allocation, af); tok = tok->next()->link(); @@ -516,13 +515,14 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocI } } -void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation) +void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) { // Ignore function call? const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end()); if (ignore) return; + int argNr = 1; for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) { if (_tokenizer->isCPP() && arg->str() == "new") { arg = arg->next(); @@ -537,10 +537,12 @@ void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const Va arg = arg->next(); // Is variable allocated? - changeAllocStatus(varInfo, allocation, tok, arg); + if (!af || af->arg == argNr) + changeAllocStatus(varInfo, allocation, tok, arg); } else if (Token::Match(arg, "%name% (")) { - functionCall(arg, varInfo, allocation); + functionCall(arg, varInfo, allocation, af); } + argNr++; } } diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h index 99779ef05..84edb54a0 100644 --- a/lib/checkleakautovar.h +++ b/lib/checkleakautovar.h @@ -105,7 +105,7 @@ private: std::set notzero); /** parse function call */ - void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation); + void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); /** parse changes in allocation status */ void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 84ea57414..8c36cde81 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -154,11 +154,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, } // Does tok2 point on a Library allocation function? - const int alloctype = settings1->library.alloc(tok2); + const int alloctype = settings1->library.alloc(tok2, -1); if (alloctype > 0) { - if (alloctype == settings1->library.dealloc("free")) + if (alloctype == settings1->library.deallocId("free")) return Malloc; - if (alloctype == settings1->library.dealloc("fclose")) + if (alloctype == settings1->library.deallocId("fclose")) return File; return Library::ismemory(alloctype) ? OtherMem : OtherRes; } @@ -227,30 +227,34 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok if (Token::simpleMatch(tok, "fcloseall ( )")) return File; - const Token* vartok = tok->tokAt(2); - while (Token::Match(vartok, "%name% .|::")) - vartok = vartok->tokAt(2); + int argNr = 1; + for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) { + const Token* vartok = tok2; + while (Token::Match(vartok, "%name% .|::")) + vartok = vartok->tokAt(2); - if (Token::Match(vartok, "%varid% )|,|-", varid)) { - if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) - return Malloc; - - if (settings1->standards.posix) { - if (tok->str() == "close") - return Fd; - if (tok->str() == "pclose") - return Pipe; - } - - // Does tok point on a Library deallocation function? - const int dealloctype = settings1->library.dealloc(tok); - if (dealloctype > 0) { - if (dealloctype == settings1->library.dealloc("free")) + if (Token::Match(vartok, "%varid% )|,|-", varid)) { + if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) return Malloc; - if (dealloctype == settings1->library.dealloc("fclose")) - return File; - return Library::ismemory(dealloctype) ? OtherMem : OtherRes; + + if (settings1->standards.posix) { + if (tok->str() == "close") + return Fd; + if (tok->str() == "pclose") + return Pipe; + } + + // Does tok point on a Library deallocation function? + const int dealloctype = settings1->library.dealloc(tok, argNr); + if (dealloctype > 0) { + if (dealloctype == settings1->library.deallocId("free")) + return Malloc; + if (dealloctype == settings1->library.deallocId("fclose")) + return File; + return Library::ismemory(dealloctype) ? OtherMem : OtherRes; + } } + argNr++; } } diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index b4d990fd0..97bb9b3f4 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1137,7 +1137,7 @@ void CheckStl::checkAutoPointer() std::set autoPtrVarId; std::map mallocVarId; // variables allocated by the malloc-like function const char STL_CONTAINER_LIST[] = "array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string"; - const int malloc = _settings->library.alloc("malloc"); // allocation function, which are not compatible with auto_ptr + const int malloc = _settings->library.allocId("malloc"); // allocation function, which are not compatible with auto_ptr const bool printStyle = _settings->isEnabled("style"); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { @@ -1153,7 +1153,7 @@ void CheckStl::checkAutoPointer() if (Token::Match(tok3, "( new %type%") && hasArrayEndParen(tok3)) { autoPointerArrayError(tok2->next()); } - if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next()) == malloc) { + if (Token::Match(tok3, "( %name% (") && malloc && _settings->library.alloc(tok3->next(), -1) == malloc) { // malloc-like function allocated memory passed to the auto_ptr constructor -> error autoPointerMallocError(tok2->next(), tok3->next()->str()); } @@ -1197,7 +1197,7 @@ void CheckStl::checkAutoPointer() if (iter != autoPtrVarId.end()) { autoPointerArrayError(tok); } - } else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2)) == malloc) { + } else if (Token::Match(tok, "%var% = %name% (") && malloc && _settings->library.alloc(tok->tokAt(2), -1) == malloc) { // C library function like 'malloc' used together with auto pointer -> error std::set::const_iterator iter = autoPtrVarId.find(tok->varId()); if (iter != autoPtrVarId.end()) { @@ -1206,7 +1206,7 @@ void CheckStl::checkAutoPointer() // it is not an auto pointer variable and it is allocated by malloc like function. mallocVarId.insert(std::make_pair(tok->varId(), tok->strAt(2))); } - } else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4)) == malloc) { + } else if (Token::Match(tok, "%var% . reset ( %name% (") && malloc && _settings->library.alloc(tok->tokAt(4), -1) == malloc) { // C library function like 'malloc' used when resetting auto pointer -> error std::set::const_iterator iter = autoPtrVarId.find(tok->varId()); if (iter != autoPtrVarId.end()) { diff --git a/lib/library.cpp b/lib/library.cpp index 78c38539b..22afb8bfe 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -139,9 +139,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc) int allocationId = 0; for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { if (strcmp(memorynode->Name(),"dealloc")==0) { - const std::map::const_iterator it = _dealloc.find(memorynode->GetText()); + const std::map::const_iterator it = _dealloc.find(memorynode->GetText()); if (it != _dealloc.end()) { - allocationId = it->second; + allocationId = it->second.groupId; break; } } @@ -158,14 +158,29 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc) for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { const std::string memorynodename = memorynode->Name(); if (memorynodename == "alloc") { - _alloc[memorynode->GetText()] = allocationId; + AllocFunc temp; + temp.groupId = allocationId; + const char *init = memorynode->Attribute("init"); if (init && strcmp(init,"false")==0) { returnuninitdata.insert(memorynode->GetText()); } - } else if (memorynodename == "dealloc") - _dealloc[memorynode->GetText()] = allocationId; - else if (memorynodename == "use") + const char *arg = memorynode->Attribute("arg"); + if (arg) + temp.arg = atoi(arg); + else + temp.arg = -1; + _alloc[memorynode->GetText()] = temp; + } else if (memorynodename == "dealloc") { + AllocFunc temp; + temp.groupId = allocationId; + const char *arg = memorynode->Attribute("arg"); + if (arg) + temp.arg = atoi(arg); + else + temp.arg = 1; + _dealloc[memorynode->GetText()] = temp; + } else if (memorynodename == "use") use.insert(memorynode->GetText()); else unknown_elements.insert(memorynodename); @@ -772,18 +787,32 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const } -/** get allocation id for function */ -int Library::alloc(const Token *tok) const +/** get allocation info for function */ +const Library::AllocFunc* Library::alloc(const Token *tok) const { const std::string funcname = functionName(tok); - return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_alloc, funcname); + return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_alloc, funcname); +} + +/** get deallocation info for function */ +const Library::AllocFunc* Library::dealloc(const Token *tok) const +{ + const std::string funcname = functionName(tok); + return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_dealloc, funcname); +} + +/** get allocation id for function */ +int Library::alloc(const Token *tok, int arg) const +{ + const Library::AllocFunc* af = alloc(tok); + return (af && af->arg == arg) ? af->groupId : 0; } /** get deallocation id for function */ -int Library::dealloc(const Token *tok) const +int Library::dealloc(const Token *tok, int arg) const { - const std::string funcname = functionName(tok); - return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getid(_dealloc, funcname); + const Library::AllocFunc* af = dealloc(tok); + return (af && af->arg == arg) ? af->groupId : 0; } diff --git a/lib/library.h b/lib/library.h index b47a8d46d..3fb8a7ddf 100644 --- a/lib/library.h +++ b/lib/library.h @@ -64,29 +64,54 @@ public: /** this is primarily meant for unit tests. it only returns true/false */ bool loadxmldata(const char xmldata[], std::size_t len); - /** get allocation id for function by name (deprecated, use other alloc) */ - int alloc(const char name[]) const { - return getid(_alloc, name); - } + struct AllocFunc { + int groupId; + int arg; + }; + + /** get allocation info for function */ + const AllocFunc* alloc(const Token *tok) const; + + /** get deallocation info for function */ + const AllocFunc* dealloc(const Token *tok) const; /** get allocation id for function */ - int alloc(const Token *tok) const; + int alloc(const Token *tok, int arg) const; /** get deallocation id for function */ - int dealloc(const Token *tok) const; + int dealloc(const Token *tok, int arg) const; + + /** get allocation info for function by name (deprecated, use other alloc) */ + const AllocFunc* alloc(const char name[]) const { + return getAllocDealloc(_alloc, name); + } + + /** get deallocation info for function by name (deprecated, use other alloc) */ + const AllocFunc* dealloc(const char name[]) const { + return getAllocDealloc(_dealloc, name); + } + + /** get allocation id for function by name (deprecated, use other alloc) */ + int allocId(const char name[]) const { + const AllocFunc* af = getAllocDealloc(_alloc, name); + return af ? af->groupId : 0; + } /** get deallocation id for function by name (deprecated, use other alloc) */ - int dealloc(const char name[]) const { - return getid(_dealloc, name); + int deallocId(const char name[]) const { + const AllocFunc* af = getAllocDealloc(_dealloc, name); + return af ? af->groupId : 0; } /** set allocation id for function */ - void setalloc(const std::string &functionname, int id) { - _alloc[functionname] = id; + void setalloc(const std::string &functionname, int id, int arg) { + _alloc[functionname].groupId = id; + _alloc[functionname].arg = arg; } - void setdealloc(const std::string &functionname, int id) { - _dealloc[functionname] = id; + void setdealloc(const std::string &functionname, int id, int arg) { + _dealloc[functionname].groupId = id; + _dealloc[functionname].arg = arg; } /** add noreturn function setting */ @@ -98,11 +123,17 @@ public: static bool ismemory(int id) { return ((id > 0) && ((id & 1) == 0)); } + static bool ismemory(const AllocFunc* func) { + return ((func->groupId > 0) && ((func->groupId & 1) == 0)); + } /** is allocation type resource? */ static bool isresource(int id) { return ((id > 0) && ((id & 1) == 1)); } + static bool isresource(const AllocFunc* func) { + return ((func->groupId > 0) && ((func->groupId & 1) == 1)); + } bool formatstr_function(const std::string& funcname) const { return _formatstr.find(funcname) != _formatstr.cend(); @@ -443,8 +474,8 @@ private: int allocid; std::set _files; std::set _useretval; - std::map _alloc; // allocation functions - std::map _dealloc; // deallocation functions + std::map _alloc; // allocation functions + std::map _dealloc; // deallocation functions std::map _noreturn; // is function noreturn? std::set _ignorefunction; // ignore functions/macros from a library (gtk, qt etc) std::map _reporterrors; @@ -462,9 +493,9 @@ private: const ArgumentChecks * getarg(const Token *ftok, int argnr) const; - static int getid(const std::map &data, const std::string &name) { - const std::map::const_iterator it = data.find(name); - return (it == data.end()) ? 0 : it->second; + static const AllocFunc* getAllocDealloc(const std::map &data, const std::string &name) { + const std::map::const_iterator it = data.find(name); + return (it == data.end()) ? nullptr : &it->second; } }; diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 6e2ccabaf..b81beebca 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -32,11 +32,11 @@ private: void run() { int id = 0; while (!settings.library.ismemory(++id)); - settings.library.setalloc("malloc", id); - settings.library.setdealloc("free", id); + settings.library.setalloc("malloc", id, -1); + settings.library.setdealloc("free", id, 1); while (!settings.library.isresource(++id)); - settings.library.setalloc("fopen", id); - settings.library.setdealloc("fclose", id); + settings.library.setalloc("fopen", id, -1); + settings.library.setdealloc("fclose", id, 1); // Assign TEST_CASE(assign1); @@ -1269,8 +1269,7 @@ private: " HeapFree(MyHeap, 0, b);" " HeapDestroy(MyHeap);" "}"); - TODO_ASSERT_EQUALS("", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n" - "[test.c:1]: (error) Resource handle 'MyHeap' freed twice.\n", errout.str()); + ASSERT_EQUALS("", errout.str()); check("void f() {" " int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" @@ -1287,8 +1286,7 @@ private: " HeapFree(MyHeap, 0, a);" " HeapDestroy(MyHeap);" "}"); - TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: b", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n" - "[test.c:1]: (error) Memory leak: b\n", errout.str()); + ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" @@ -1297,7 +1295,8 @@ private: " HeapFree(MyHeap, 0, a);" " HeapFree(MyHeap, 0, b);" "}"); - TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n", errout.str()); + TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", + "", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" @@ -1305,8 +1304,9 @@ private: " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" "}"); - TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap\n[test.c:1] (error) Memory leak: b", - "[test.c:1]: (error) Mismatching allocation and deallocation: MyHeap\n[test.c:1]: (error) Memory leak: b\n", errout.str()); + TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n" + "[test.c:1] (error) Memory leak: b", + "[test.c:1]: (error) Memory leak: b\n", errout.str()); } }; diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp index 45e2beae3..0cf74ceff 100644 --- a/test/testlibrary.cpp +++ b/test/testlibrary.cpp @@ -43,6 +43,7 @@ private: TEST_CASE(function_warn); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions + TEST_CASE(memory3); TEST_CASE(resource); TEST_CASE(podtype); TEST_CASE(container); @@ -358,7 +359,11 @@ private: ASSERT(library.argumentChecks.empty()); ASSERT(Library::ismemory(library.alloc("CreateX"))); - ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX")); + ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); + const Library::AllocFunc* af = library.alloc("CreateX"); + ASSERT(af && af->arg == -1); + const Library::AllocFunc* df = library.dealloc("DeleteX"); + ASSERT(df && df->arg == 1); } void memory2() const { const char xmldata1[] = "\n" @@ -380,8 +385,30 @@ private: library.loadxmldata(xmldata1, sizeof(xmldata1)); library.loadxmldata(xmldata2, sizeof(xmldata2)); - ASSERT_EQUALS(library.dealloc("free"), library.alloc("malloc")); - ASSERT_EQUALS(library.dealloc("free"), library.alloc("foo")); + ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc")); + ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo")); + } + void memory3() const { + const char xmldata[] = "\n" + "\n" + " \n" + " CreateX\n" + " DeleteX\n" + " \n" + ""; + + Library library; + readLibrary(library, xmldata); + ASSERT(library.use.empty()); + ASSERT(library.leakignore.empty()); + ASSERT(library.argumentChecks.empty()); + + const Library::AllocFunc* af = library.alloc("CreateX"); + ASSERT(af && af->arg == 5); + const Library::AllocFunc* df = library.dealloc("DeleteX"); + ASSERT(df && df->arg == 2); + + ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend()); } void resource() const { @@ -399,8 +426,8 @@ private: ASSERT(library.leakignore.empty()); ASSERT(library.argumentChecks.empty()); - ASSERT(Library::isresource(library.alloc("CreateX"))); - ASSERT_EQUALS(library.alloc("CreateX"), library.dealloc("DeleteX")); + ASSERT(Library::isresource(library.allocId("CreateX"))); + ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); } void podtype() const {