diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 1996fee98..6aa3fe37f 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -507,1620 +507,6 @@ bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname, con return ((call_func_white_list.find(funcname)!=call_func_white_list.end()) || settings->library.isLeakIgnore(funcname) || (cpp && funcname == "delete")); } -namespace { - const std::set call_func_keywords = { - "asprintf" - , "delete" - , "fclose" - , "for" - , "free" - , "if" - , "realloc" - , "return" - , "switch" - , "while" - , "sizeof" - }; -} - -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(), mSettings, mTokenizer->isCPP())) { - if (call_func_keywords.find(tok->str())!=call_func_keywords.end()) - return nullptr; - - // is the varid a parameter? - for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != tok->linkAt(1); tok2 = tok2->next()) { - if (tok2->str() == "(") { - tok2 = tok2->nextArgument(); - if (!tok2) - break; - } - if (tok2->varId() == varid) { - if (tok->strAt(-1) == ".") - return "use"; - else if (tok2->strAt(1) == "=") - return "assign"; - else if (tok->str()=="printf") - return "use"; // <- it is not certain printf dereference the pointer TODO: check the format string - else - return "use_"; - } - } - - return nullptr; - } - - if (mSettings->library.isnoreturn(tok) && tok->strAt(-1) != "=") - return "exit"; - - if (varid > 0 && (getReallocationType(tok, varid) != No || getDeallocationType(tok, varid) != No)) - return nullptr; - - if (callstack.size() > 2) - return "dealloc_"; - - const std::string& funcname(tok->str()); - for (const Token *tok2 : callstack) { - if (tok2 && tok2->str() == funcname) - return "recursive"; - } - callstack.push_back(tok); - - // lock/unlock.. - if (varid == 0) { - const Function* func = tok->function(); - if (!func || !func->hasBody()) - return nullptr; - - Token *ftok = getcode(func->functionScope->bodyStart->next(), callstack, 0, alloctype, dealloctype, false, 1); - simplifycode(ftok); - const char *ret = nullptr; - if (Token::simpleMatch(ftok, "; alloc ; }")) - ret = "alloc"; - else if (Token::simpleMatch(ftok, "; dealloc ; }")) - ret = "dealloc"; - TokenList::deleteTokens(ftok); - return ret; - } - - // how many parameters is there in the function call? - const unsigned int numpar = countParameters(tok); - if (numpar == 0) { - // Taking return value => it is not a noreturn function - if (tok->strAt(-1) == "=") - return nullptr; - - // Function is not noreturn - if (tok->function() && tok->function()->functionScope) { - std::string temp; - if (!mSettings->library.isScopeNoReturn(tok->function()->functionScope->bodyEnd, &temp) && temp.empty()) - return nullptr; - } else if (mSettings->library.isnotnoreturn(tok)) - return nullptr; - - return "callfunc"; - } - - unsigned int par = 0; - - const bool dot(tok->previous()->str() == "."); - const bool eq(tok->previous()->str() == "="); - - const Token *functok = tok; - - tok = Token::findsimplematch(tok, "("); - if (tok) - tok = tok->next(); - - for (; tok; tok = tok->nextArgument()) { - ++par; - if (Token::Match(tok, "%varid% [,()]", varid)) { - if (dot) - return "use"; - - const Function* function = functok->function(); - if (!function) - return "use"; - - // how many parameters does the function want? - if (numpar != function->argCount()) // TODO: Handle default parameters - return "recursive"; - - if (!function->functionScope) - return "use"; - const Variable* param = function->getArgumentVar(par-1); - if (!param || !param->nameToken()) - return "use"; - Token *func = getcode(function->functionScope->bodyStart->next(), callstack, param->declarationId(), alloctype, dealloctype, false, sz); - //simplifycode(func); - const Token *func_ = func; - while (func_ && func_->str() == ";") - func_ = func_->next(); - - const char *ret = nullptr; - /** @todo handle "goto" */ - if (Token::findsimplematch(func_, "dealloc")) - ret = "dealloc"; - else if (Token::findsimplematch(func_, "use")) - ret = "use"; - else if (Token::findsimplematch(func_, "&use")) - ret = "&use"; - - TokenList::deleteTokens(func); - return ret; - } - if (Token::Match(tok, "& %varid% [,()]", varid)) { - const Function *func = functok->function(); - if (func == nullptr) - continue; - AllocType a; - const char *ret = functionArgAlloc(func, par, a); - - if (a != No) { - if (alloctype == No) - alloctype = a; - else if (alloctype != a) - alloctype = Many; - allocpar = true; - return ret; - } - } - if (Token::Match(tok, "%varid% . %name% [,)]", varid)) - return "use"; - } - return (eq || mSettings->experimental) ? nullptr : "callfunc"; -} - -template -static void addtoken(Token **rettail, const Token *tok, T&& str) -{ - (*rettail)->insertToken(str); - (*rettail) = (*rettail)->next(); - (*rettail)->linenr(tok->linenr()); - (*rettail)->fileIndex(tok->fileIndex()); -} - - -Token *CheckMemoryLeakInFunction::getcode(const Token *tok, std::list callstack, const unsigned int varid, CheckMemoryLeak::AllocType &alloctype, CheckMemoryLeak::AllocType &dealloctype, bool classmember, unsigned int sz) -{ - // variables whose value depends on if(!var). If one of these variables - // is used in a if-condition then generate "ifv" instead of "if". - std::set extravar; - - // The first token should be ";" - Token* rethead = new Token(); - rethead->str(";"); - rethead->linenr(tok->linenr()); - rethead->fileIndex(tok->fileIndex()); - Token* rettail = rethead; - - int indentlevel = 0; - int parlevel = 0; - for (; tok; tok = tok->next()) { - if (tok->str() == "{") { - addtoken(&rettail, tok, "{"); - ++indentlevel; - } else if (tok->str() == "}") { - addtoken(&rettail, tok, "}"); - if (indentlevel <= 0) - break; - --indentlevel; - } - - else if (tok->str() == "(") - ++parlevel; - else if (tok->str() == ")") - --parlevel; - - if (parlevel == 0 && tok->str() == ";") - addtoken(&rettail, tok, ";"); - - // Start of new statement.. check if the statement has anything interesting - if (varid > 0 && parlevel == 0 && Token::Match(tok, "[;{}]")) { - if (Token::Match(tok->next(), "[{};]")) - continue; - - // function calls are interesting.. - const Token *tok2 = tok; - if (Token::Match(tok2, "[{};] :: %name%")) - tok2 = tok2->next(); - while (Token::Match(tok2->next(), "%name% ::|. %name%")) - tok2 = tok2->tokAt(2); - if (Token::Match(tok2->next(), "%name% (")) - ; - - else if (Token::Match(tok->next(), "continue|break|return|throw|goto|do|else")) - ; - - else { - const Token *skipToToken = nullptr; - - // scan statement for interesting keywords / varid - for (tok2 = tok->next(); tok2; tok2 = tok2->next()) { - if (tok2->str() == ";") { - // nothing interesting found => skip this statement - skipToToken = tok2->previous(); - break; - } - - if (tok2->varId() == varid || - tok2->str() == ":" || tok2->str() == "{" || tok2->str() == "}") { - break; - } - } - - if (skipToToken) { - tok = skipToToken; - continue; - } - } - } - - if (varid == 0) { - if (!callstack.empty() && Token::Match(tok, "[;{}] __cppcheck_lock|__cppcheck_unlock ( ) ;")) { - // Type of leak = Resource leak - alloctype = dealloctype = CheckMemoryLeak::File; - - if (tok->next()->str() == "__cppcheck_lock") { - addtoken(&rettail, tok, "alloc"); - } else { - addtoken(&rettail, tok, "dealloc"); - } - - tok = tok->tokAt(3); - continue; - } - - if (Token::simpleMatch(tok, "if (")) { - addtoken(&rettail, tok, "if"); - tok = tok->next()->link(); - continue; - } - } else { - - if (Token::Match(tok, "%varid% = close ( %varid% )", varid)) { - addtoken(&rettail, tok, "dealloc"); - addtoken(&rettail, tok, ";"); - addtoken(&rettail, tok, "assign"); - addtoken(&rettail, tok, ";"); - tok = tok->tokAt(5); - continue; - } - - // var = strcpy|.. ( var , - if (Token::Match(tok, "[;{}] %varid% = memcpy|memmove|memset|strcpy|strncpy|strcat|strncat ( %varid% ,", varid)) { - tok = tok->linkAt(4); - continue; - } - - if (Token::Match(tok->previous(), "[(;{}] %varid% =", varid) || - Token::Match(tok, "asprintf|vasprintf ( & %varid% ,", varid)) { - CheckMemoryLeak::AllocType alloc; - - if (Token::Match(tok, "asprintf|vasprintf (")) { - // todo: check how the return value is used. - if (!Token::Match(tok->previous(), "[;{}]")) { - TokenList::deleteTokens(rethead); - return nullptr; - } - alloc = Malloc; - tok = tok->next()->link(); - } else { - alloc = getAllocationType(tok->tokAt(2), varid); - } - - if (sz > 1 && - Token::Match(tok->tokAt(2), "malloc ( %num% )") && - (MathLib::toLongNumber(tok->strAt(4)) % long(sz)) != 0) { - mismatchSizeError(tok->tokAt(4), tok->strAt(4)); - } - - if (alloc == CheckMemoryLeak::No) { - alloc = getReallocationType(tok->tokAt(2), varid); - if (alloc != CheckMemoryLeak::No) { - addtoken(&rettail, tok, "realloc"); - addtoken(&rettail, tok, ";"); - tok = tok->tokAt(2); - if (Token::Match(tok, "%name% (")) - tok = tok->next()->link(); - continue; - } - } - - // don't check classes.. - if (alloc == CheckMemoryLeak::New) { - if (Token::Match(tok->tokAt(2), "new struct| %type% [(;]")) { - const int offset = tok->strAt(3) == "struct" ? 1 : 0; - if (isclass(tok->tokAt(3 + offset), varid)) { - alloc = No; - } - } else if (Token::Match(tok->tokAt(2), "new ( nothrow ) struct| %type%")) { - const int offset = tok->strAt(6) == "struct" ? 1 : 0; - if (isclass(tok->tokAt(6 + offset), varid)) { - alloc = No; - } - } else if (Token::Match(tok->tokAt(2), "new ( std :: nothrow ) struct| %type%")) { - const int offset = tok->strAt(8) == "struct" ? 1 : 0; - if (isclass(tok->tokAt(8 + offset), varid)) { - alloc = No; - } - } - - if (Token::simpleMatch(tok->next(), "= new")) { - tok = tok->tokAt(2); - while (Token::Match(tok->next(), "%name%|::|(|[")) { - if (Token::Match(tok->next(), "(|[")) - tok = tok->linkAt(1); - else - tok = tok->next(); - } - } - - if (alloc == No && alloctype == No) - alloctype = CheckMemoryLeak::New; - } - - if (alloc != No) { - addtoken(&rettail, tok, "alloc"); - - if (alloctype != No && alloctype != alloc) - alloc = Many; - - if (alloc != Many && dealloctype != No && dealloctype != Many && dealloctype != alloc) { - callstack.push_back(tok); - mismatchAllocDealloc(callstack, Token::findmatch(mTokenizer->tokens(), "%varid%", varid)->str()); - callstack.pop_back(); - } - - alloctype = alloc; - - if (Token::Match(tok, "%name% = %type% (")) { - tok = tok->linkAt(3); - continue; - } - } - - // assignment.. - else { - // is the pointer in rhs? - bool rhs = false; - bool trailingSemicolon = false; - bool used = false; - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { - if (tok2->str() == ";") { - trailingSemicolon = true; - if (rhs) - tok = tok2; - break; - } - - if (!used && !rhs) { - if (Token::Match(tok2, "[=+(,] %varid%", varid)) { - if (Token::Match(tok2, "[(,]")) { - used = true; - addtoken(&rettail, tok, "use"); - addtoken(&rettail, tok, ";"); - } - rhs = true; - } - } - } - - if (!used) { - if (!rhs) - addtoken(&rettail, tok, "assign"); - else { - addtoken(&rettail, tok, "use_"); - if (trailingSemicolon) - addtoken(&rettail, tok, ";"); - } - } - continue; - } - } - - if (Token::Match(tok->previous(), "%op%|;|,|{|}|) ::| %name%") || (Token::Match(tok->previous(), "( ::| %name%") && (!rettail || rettail->str() != "loop"))) { - if (tok->str() == "::") - tok = tok->next(); - - if (Token::Match(tok, "%varid% ?", varid)) - tok = tok->tokAt(2); - - AllocType dealloc = getDeallocationType(tok, varid); - - if (dealloc != No && tok->str() == "fcloseall" && alloctype != dealloc) - ; - - else if (dealloc != No) { - addtoken(&rettail, tok, "dealloc"); - - if (dealloctype != No && dealloctype != dealloc) - dealloc = Many; - - if (dealloc != Many && alloctype != No && alloctype != Many && alloctype != dealloc) { - callstack.push_back(tok); - mismatchAllocDealloc(callstack, Token::findmatch(mTokenizer->tokens(), "%varid%", varid)->str()); - callstack.pop_back(); - } - dealloctype = dealloc; - - if (tok->strAt(2) == "(") - tok = tok->linkAt(2); - continue; - } - } - - // if else switch - if (Token::simpleMatch(tok, "if (")) { - if (alloctype == Fd) { - if (ifvar(tok, varid, ">", "-1") || - ifvar(tok, varid, ">=", "0") || - ifvar(tok, varid, ">", "0") || - ifvar(tok, varid, "!=", "-1")) { - addtoken(&rettail, tok, "if(var)"); - tok = tok->next()->link(); - continue; - } else if (ifvar(tok, varid, "==", "-1") || - ifvar(tok, varid, "<", "0")) { - addtoken(&rettail, tok, "if(!var)"); - tok = tok->next()->link(); - continue; - } - } - - if (ifvar(tok, varid, "!=", "0")) { - addtoken(&rettail, tok, "if(var)"); - - // Make sure the "use" will not be added - tok = tok->next()->link(); - continue; - } else if (ifvar(tok, varid, "==", "0")) { - addtoken(&rettail, tok, "if(!var)"); - - // parse the if-body. - // if a variable is assigned then add variable to "extravar". - for (const Token *tok2 = tok->next()->link()->tokAt(2); tok2; tok2 = tok2->next()) { - if (tok2->str() == "{") - tok2 = tok2->link(); - else if (tok2->str() == "}") - break; - else if (Token::Match(tok2, "%var% =")) - extravar.insert(tok2->varId()); - } - - tok = tok->next()->link(); - continue; - } else { - // Check if the condition depends on var or extravar somehow.. - bool dep = false; - const Token* const end = tok->linkAt(1); - for (const Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { - if (Token::Match(tok2, "close|pclose|fclose|closedir ( %varid% )", varid)) { - addtoken(&rettail, tok, "dealloc"); - addtoken(&rettail, tok, ";"); - dep = true; - break; - } else if (alloctype == Fd && Token::Match(tok2, "%varid% !=|>=", varid)) { - dep = true; - } else if (Token::Match(tok2, "! %varid%", varid)) { - dep = true; - } else if (Token::Match(tok2, "%name% (") && !test_white_list(tok2->str(), mSettings, mTokenizer->isCPP())) { - bool use = false; - for (const Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->nextArgument()) { - if (Token::Match(tok3->previous(), "(|, &| %varid% ,|)", varid)) { - use = true; - break; - } - } - if (use) { - addtoken(&rettail, tok, "use"); - addtoken(&rettail, tok, ";"); - dep = false; - break; - } - } else if (tok2->varId() && extravar.find(tok2->varId()) != extravar.end()) { - dep = true; - } else if (tok2->varId() == varid && - (tok2->next()->isConstOp() || tok2->previous()->isConstOp())) - dep = true; - } - - if (notvar(tok->next()->astOperand2(), varid)) - addtoken(&rettail, tok, "if(!var)"); - else - addtoken(&rettail, tok, (dep ? "ifv" : "if")); - - tok = tok->next()->link(); - continue; - } - } - } - - if ((tok->str() == "else") || (tok->str() == "switch")) { - addtoken(&rettail, tok, tok->str()); - if (Token::simpleMatch(tok, "switch (")) - tok = tok->next()->link(); - continue; - } - - if ((tok->str() == "case")) { - addtoken(&rettail, tok, "case"); - addtoken(&rettail, tok, ";"); - if (Token::Match(tok, "case %any% :")) - tok = tok->tokAt(2); - continue; - } - - if ((tok->str() == "default")) { - addtoken(&rettail, tok, "default"); - addtoken(&rettail, tok, ";"); - continue; - } - - // Loops.. - else if ((tok->str() == "for") || (tok->str() == "while")) { - const Token* const end = tok->linkAt(1); - - if ((Token::simpleMatch(tok, "while (") && alwaysTrue(tok->next()->astOperand2())) || - Token::simpleMatch(tok, "for ( ; ; )")) { - addtoken(&rettail, tok, "while1"); - tok = end; - continue; - } - - else if (varid && getDeallocationType(tok->tokAt(2), varid) != No) { - addtoken(&rettail, tok, "dealloc"); - addtoken(&rettail, tok, ";"); - } - - else if (alloctype == Fd && varid) { - if (Token::Match(tok, "while ( 0 <= %varid% )", varid) || - Token::Match(tok, "while ( %varid% >= 0 )", varid) || - Token::Match(tok, "while ( %varid% != -1 )", varid) || - Token::Match(tok, "while ( -1 != %varid% )", varid)) { - addtoken(&rettail, tok, "while(var)"); - tok = end; - continue; - } else if (Token::Match(tok, "while ( %varid% == -1 )", varid) || - Token::Match(tok, "while ( -1 == %varid% )", varid) || - Token::Match(tok, "while ( %varid% < 0 )", varid) || - Token::Match(tok, "while ( 0 > %varid% )", varid)) { - addtoken(&rettail, tok, "while(!var)"); - tok = end; - continue; - } - } - - else if (varid && Token::Match(tok, "while ( %varid% )", varid)) { - addtoken(&rettail, tok, "while(var)"); - tok = end; - continue; - } else if (varid && Token::simpleMatch(tok, "while (") && notvar(tok->next()->astOperand2(), varid)) { - addtoken(&rettail, tok, "while(!var)"); - tok = end; - continue; - } - - addtoken(&rettail, tok, "loop"); - - if (varid > 0 && notvar(tok->next()->astOperand2(), varid)) - addtoken(&rettail, tok, "!var"); - - continue; - } - if ((tok->str() == "do")) { - addtoken(&rettail, tok, "do"); - continue; - } - - // continue / break.. - else if (tok->str() == "continue") { - addtoken(&rettail, tok, "continue"); - } else if (tok->str() == "break") { - addtoken(&rettail, tok, "break"); - } else if (tok->str() == "goto") { - addtoken(&rettail, tok, "goto"); - } - - // Return.. - else if (tok->str() == "return") { - addtoken(&rettail, tok, "return"); - if (varid == 0) { - addtoken(&rettail, tok, ";"); - while (tok && tok->str() != ";") - tok = tok->next(); - if (!tok) - break; - continue; - } - - // Returning a auto_ptr of this allocated variable.. - if (Token::simpleMatch(tok->next(), "std :: auto_ptr <")) { - const Token *tok2 = tok->linkAt(4); - if (Token::Match(tok2, "> ( %varid% )", varid)) { - addtoken(&rettail, tok, "use"); - tok = tok2->tokAt(3); - } - } - - else if (varid && Token::Match(tok, "return strcpy|strncpy|memcpy ( %varid%", varid)) { - addtoken(&rettail, tok, "use"); - tok = tok->tokAt(2); - } - - else { - bool use = false; - - std::stack functions; - - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { - if (tok2->str() == ";") { - tok = tok2; - break; - } - - if (tok2->str() == "(") - functions.push(tok2->previous()); - else if (!functions.empty() && tok2->str() == ")") - functions.pop(); - - if (tok2->varId() == varid) { - // Read data.. - if (!Token::Match(tok2->previous(), "&|(") && - tok2->strAt(1) == "[") { - ; - } else if (functions.empty() || - !test_white_list(functions.top()->str(), mSettings, mTokenizer->isCPP()) || - getDeallocationType(functions.top(),varid) != AllocType::No) { - use = true; - } - } - } - if (use) - addtoken(&rettail, tok, "use"); - addtoken(&rettail, tok, ";"); - } - } - - // throw.. - else if (mTokenizer->isCPP() && Token::Match(tok, "try|throw|catch")) { - addtoken(&rettail, tok, tok->str()); - if (tok->strAt(1) == "(") - tok = tok->next()->link(); - } - - // Assignment.. - if (varid) { - if (Token::simpleMatch(tok, "= {")) { - const Token* const end2 = tok->linkAt(1); - bool use = false; - for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { - if (tok2->varId() == varid) { - use = true; - break; - } - } - - if (use) { - addtoken(&rettail, tok, "use"); - addtoken(&rettail, tok, ";"); - tok = tok->next()->link(); - continue; - } - } - - if (Token::Match(tok, "& %name% = %varid% ;", varid)) { - while (rethead->next()) - rethead->deleteNext(); - return rethead; - } - if (Token::Match(tok, "[)=] %varid% [+;)]", varid) || - (Token::Match(tok, "%name% + %varid%", varid) && - tok->strAt(3) != "[" && - tok->strAt(3) != ".") || - Token::Match(tok, "<< %varid% ;", varid) || - Token::Match(tok, "= strcpy|strcat|memmove|memcpy ( %varid% ,", varid) || - Token::Match(tok, "[;{}] %name% [ %varid% ]", varid)) { - addtoken(&rettail, tok, "use"); - } else if (Token::Match(tok->previous(), ";|{|}|=|(|,|%cop% %varid% .|[", varid)) { - // warning is written for "dealloc ; use_ ;". - // but this use doesn't affect the leak-checking - addtoken(&rettail, tok, "use_"); - } - } - - // Investigate function calls.. - if (Token::Match(tok, "%name% (")) { - // A function call should normally be followed by ";" - if (Token::simpleMatch(tok->next()->link(), ") {")) { - if (!Token::Match(tok, "if|for|while|switch")) { - addtoken(&rettail, tok, "exit"); - addtoken(&rettail, tok, ";"); - tok = tok->next()->link(); - continue; - } - } - - // Calling setjmp / longjmp => bail out - else if (Token::Match(tok, "setjmp|longjmp")) { - while (rethead->next()) - rethead->deleteNext(); - return rethead; - } - - // Inside class function.. if the var is passed as a parameter then - // just add a "::use" - // The "::use" means that a member function was probably called but it wasn't analysed further - else if (classmember) { - if (mSettings->library.isnoreturn(tok)) - addtoken(&rettail, tok, "exit"); - - else if (!test_white_list(tok->str(), mSettings, mTokenizer->isCPP())) { - const Token* const end2 = tok->linkAt(1); - for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { - if (tok2->varId() == varid) { - addtoken(&rettail, tok, "::use"); - break; - } - } - } - } - - else { - if (varid > 0 && Token::Match(tok, "%name% ( close|fclose|pclose ( %varid% ) ) ;", varid)) { - addtoken(&rettail, tok, "dealloc"); - tok = tok->next()->link(); - continue; - } - - bool allocpar = false; - const char *str = call_func(tok, callstack, varid, alloctype, dealloctype, allocpar, sz); - if (str) { - if (allocpar) { - addtoken(&rettail, tok, str); - tok = tok->next()->link(); - } else if (varid == 0 || str != std::string("alloc")) { - addtoken(&rettail, tok, str); - } else if (Token::Match(tok->tokAt(-2), "%varid% =", varid)) { - addtoken(&rettail, tok, str); - } - } else if (varid > 0 && - getReallocationType(tok, varid) != No && - tok->tokAt(2)->varId() == varid) { - addtoken(&rettail, tok, "if"); - addtoken(&rettail, tok, "{"); - addtoken(&rettail, tok, "dealloc"); - addtoken(&rettail, tok, ";"); - addtoken(&rettail, tok, "}"); - tok = tok->next()->link(); - continue; - } - } - } - - // Callback.. - if (Token::Match(tok, "( *| %name%") && Token::simpleMatch(tok->link(),") (")) { - const Token *tok2 = tok->next(); - if (tok2->str() == "*") - tok2 = tok2->next(); - tok2 = tok2->next(); - - while (Token::Match(tok2, ". %name%")) - tok2 = tok2->tokAt(2); - - if (Token::simpleMatch(tok2, ") (")) { - for (; tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "[;{]")) - break; - else if (tok2->varId() == varid) { - addtoken(&rettail, tok, "use"); - break; - } - } - } - } - - // Linux lists.. - if (varid > 0 && Token::Match(tok, "[=(,] & (| %varid% [.[,)]", varid)) { - // Is variable passed to a "leak-ignore" function? - bool leakignore = false; - if (Token::Match(tok, "[(,]")) { - const Token *parent = tok; - while (parent && parent->str() != "(") - parent = parent->astParent(); - if (parent && parent->astOperand1() && parent->astOperand1()->isName()) { - const std::string &functionName = parent->astOperand1()->str(); - if (mSettings->library.isLeakIgnore(functionName)) - leakignore = true; - } - } - // Not passed to "leak-ignore" function, add "&use". - if (!leakignore) - addtoken(&rettail, tok, "&use"); - } - } - - for (Token *tok1 = rethead; tok1; tok1 = tok1->next()) { - if (Token::simpleMatch(tok1, "callfunc alloc ;")) { - tok1->deleteThis(); - tok1->insertToken("use"); - tok1->insertToken(";"); - } - } - - return rethead; -} - - - - -void CheckMemoryLeakInFunction::simplifycode(Token *tok) const -{ - if (mTokenizer->isCPP()) { - // Replace "throw" that is not in a try block with "return" - int indentlevel = 0; - int trylevel = -1; - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (tok2->str() == "{") - ++indentlevel; - else if (tok2->str() == "}") { - --indentlevel; - if (indentlevel <= trylevel) - trylevel = -1; - } else if (trylevel == -1 && tok2->str() == "try") - trylevel = indentlevel; - else if (trylevel == -1 && tok2->str() == "throw") - tok2->str("return"); - } - } - - const bool printExperimental = mSettings->experimental; - - // Insert extra ";" - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (!tok2->previous() || Token::Match(tok2->previous(), "[;{}]")) { - if (Token::Match(tok2, "assign|callfunc|use assign|callfunc|use|}")) { - tok2->insertToken(";"); - } - } - } - - // remove redundant braces.. - for (Token *start = tok; start; start = start->next()) { - if (Token::simpleMatch(start, "; {")) { - // the "link" doesn't work here. Find the end brace.. - unsigned int indent = 0; - for (Token *end = start; end; end = end->next()) { - if (end->str() == "{") - ++indent; - else if (end->str() == "}") { - if (indent <= 1) { - // If the start/end braces are redundant, delete them - if (indent == 1 && Token::Match(end->previous(), "[;{}] } %any%")) { - start->deleteNext(); - end->deleteThis(); - } - break; - } - --indent; - } - } - } - } - - // reduce the code.. - // it will be reduced in N passes. When a pass completes without any - // simplifications the loop is done. - bool done = false; - while (! done) { - //tok->printOut("simplifycode loop.."); - done = true; - - // reduce callfunc - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (tok2->str() == "callfunc") { - if (!Token::Match(tok2->previous(), "[;{}] callfunc ; }")) - tok2->deleteThis(); - } - } - - // If the code starts with "if return ;" then remove it - if (Token::Match(tok, ";| if return ;")) { - tok->deleteNext(); - tok->deleteThis(); - if (tok->str() == "return") - tok->deleteThis(); - if (tok->strAt(1) == "else") - tok->deleteNext(); - } - - // simplify "while1" contents.. - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (Token::simpleMatch(tok2, "while1 {")) { - unsigned int innerIndentlevel = 0; - for (Token *tok3 = tok2->tokAt(2); tok3; tok3 = tok3->next()) { - if (tok3->str() == "{") - ++innerIndentlevel; - else if (tok3->str() == "}") { - if (innerIndentlevel == 0) - break; - --innerIndentlevel; - } - while (innerIndentlevel == 0 && Token::Match(tok3, "[{};] if|ifv|else { continue ; }")) { - tok3->deleteNext(5); - if (tok3->strAt(1) == "else") - tok3->deleteNext(); - } - } - - if (Token::simpleMatch(tok2, "while1 { if { dealloc ; return ; } }")) { - tok2->str(";"); - tok2->deleteNext(3); - tok2->tokAt(4)->deleteNext(2); - } - } - } - - // Main inner simplification loop - for (Token *tok2 = tok; tok2; tok2 = tok2 ? tok2->next() : nullptr) { - // Delete extra ";" - while (Token::Match(tok2, "[;{}] ;")) { - tok2->deleteNext(); - done = false; - } - - // Replace "{ }" with ";" - if (Token::simpleMatch(tok2->next(), "{ }")) { - tok2->deleteNext(2); - tok2->insertToken(";"); - done = false; - } - - // Delete braces around a single instruction.. - if (Token::Match(tok2->next(), "{ %name% ; }")) { - tok2->deleteNext(); - tok2->tokAt(2)->deleteNext(); - done = false; - } - if (Token::Match(tok2->next(), "{ %name% %name% ; }")) { - tok2->deleteNext(); - tok2->tokAt(3)->deleteNext(); - done = false; - } - - // Reduce "if if|callfunc" => "if" - else if (Token::Match(tok2, "if if|callfunc")) { - tok2->deleteNext(); - done = false; - } - - // outer/inner if blocks. Remove outer condition.. - else if (Token::Match(tok2->next(), "if|if(var) { if return use ; }")) { - tok2->deleteNext(2); - tok2->tokAt(4)->deleteNext(); - done = false; - } - - else if (tok2->next() && tok2->next()->str() == "if") { - // Delete empty if that is not followed by an else - if (Token::Match(tok2->next(), "if ; !!else")) { - tok2->deleteNext(); - done = false; - } - - // Reduce "if X ; else X ;" => "X ;" - else if (Token::Match(tok2->next(), "if %name% ; else %name% ;") && - tok2->strAt(2) == tok2->strAt(5)) { - tok2->deleteNext(4); - done = false; - } - - // Reduce "if continue ; if continue ;" => "if continue ;" - else if (Token::simpleMatch(tok2->next(), "if continue ; if continue ;")) { - tok2->deleteNext(3); - done = false; - } - - // Reduce "if return ; alloc ;" => "alloc ;" - else if (Token::Match(tok2, "[;{}] if return ; alloc|return ;")) { - tok2->deleteNext(3); - done = false; - } - - // "[;{}] if alloc ; else return ;" => "[;{}] alloc ;" - else if (Token::Match(tok2, "[;{}] if alloc ; else return ;")) { - // Remove "if" - tok2->deleteNext(); - // Remove "; else return" - tok2->next()->deleteNext(3); - done = false; - } - - // Reduce "if ; else %name% ;" => "if %name% ;" - else if (Token::Match(tok2->next(), "if ; else %name% ;")) { - tok2->next()->deleteNext(2); - done = false; - } - - // Reduce "if ; else" => "if" - else if (Token::simpleMatch(tok2->next(), "if ; else")) { - tok2->next()->deleteNext(2); - done = false; - } - - // Reduce "if return ; else|if return|continue ;" => "if return ;" - else if (Token::Match(tok2->next(), "if return ; else|if return|continue|break ;")) { - tok2->tokAt(3)->deleteNext(3); - done = false; - } - - // Reduce "if continue|break ; else|if return ;" => "if return ;" - else if (Token::Match(tok2->next(), "if continue|break ; if|else return ;")) { - tok2->next()->deleteNext(3); - done = false; - } - - // Remove "else" after "if continue|break|return" - else if (Token::Match(tok2->next(), "if continue|break|return ; else")) { - tok2->tokAt(3)->deleteNext(); - done = false; - } - - // Delete "if { dealloc|assign|use ; return ; }" - else if (Token::Match(tok2, "[;{}] if { dealloc|assign|use ; return ; }") && - !Token::findmatch(tok, "if {| alloc ;")) { - tok2->deleteNext(7); - if (tok2->strAt(1) == "else") - tok2->deleteNext(); - done = false; - } - - // Remove "if { dealloc ; callfunc ; } !!else|return" - else if (Token::Match(tok2->next(), "if { dealloc|assign ; callfunc ; }") && - !Token::Match(tok2->tokAt(8), "else|return")) { - tok2->deleteNext(7); - done = false; - } - - continue; - } - - // Reduce "alloc while(!var) alloc ;" => "alloc ;" - if (Token::Match(tok2, "[;{}] alloc ; while(!var) alloc ;")) { - tok2->deleteNext(3); - done = false; - } - - // Reduce "ifv return;" => "if return use;" - if (Token::simpleMatch(tok2, "ifv return ;")) { - tok2->str("if"); - tok2->next()->insertToken("use"); - done = false; - } - - // Reduce "if(var) dealloc ;" and "if(var) use ;" that is not followed by an else.. - if (Token::Match(tok2, "[;{}] if(var) assign|dealloc|use ; !!else")) { - tok2->deleteNext(); - done = false; - } - - // Reduce "; if(!var) alloc ; !!else" => "; dealloc ; alloc ;" - if (Token::Match(tok2, "; if(!var) alloc ; !!else")) { - // Remove the "if(!var)" - tok2->deleteNext(); - - // Insert "dealloc ;" before the "alloc ;" - tok2->insertToken(";"); - tok2->insertToken("dealloc"); - - done = false; - } - - // Reduce "if(!var) exit ;" => ";" - if (Token::simpleMatch(tok2, "; if(!var) exit ;")) { - tok2->deleteNext(2); - done = false; - } - - // Reduce "if* ;".. - if (Token::Match(tok2->next(), "if(var)|if(!var)|ifv ;")) { - // Followed by else.. - if (tok2->strAt(3) == "else") { - tok2 = tok2->next(); - if (tok2->str() == "if(var)") - tok2->str("if(!var)"); - else if (tok2->str() == "if(!var)") - tok2->str("if(var)"); - - // remove the "; else" - tok2->deleteNext(2); - } else { - // remove the "if*" - tok2->deleteNext(); - } - done = false; - } - - // Reduce "else ;" => ";" - if (Token::simpleMatch(tok2->next(), "else ;")) { - tok2->deleteNext(); - done = false; - } - - // Reduce "while1 continue| ;" => "use ;" - if (Token::Match(tok2, "while1 if| continue| ;")) { - tok2->str("use"); - while (tok2->strAt(1) != ";") - tok2->deleteNext(); - done = false; - } - - // Reduce "while1 if break ;" => ";" - if (Token::simpleMatch(tok2, "while1 if break ;")) { - tok2->str(";"); - tok2->deleteNext(2); - done = false; - } - - // Delete if block: "alloc; if return use ;" - if (Token::Match(tok2, "alloc ; if return use ; !!else")) { - tok2->deleteNext(4); - done = false; - } - - // Reduce "alloc|dealloc|use|callfunc ; exit ;" => "; exit ;" - if (Token::Match(tok2, "[;{}] alloc|dealloc|use|callfunc ; exit ;")) { - tok2->deleteNext(); - done = false; - } - - // Reduce "alloc|dealloc|use ; if(var) exit ;" - if (Token::Match(tok2, "alloc|dealloc|use ; if(var) exit ;")) { - tok2->deleteThis(); - done = false; - } - - // Remove "if exit ;" - if (Token::simpleMatch(tok2, "if exit ;")) { - tok2->deleteNext(); - tok2->deleteThis(); - done = false; - } - - // Remove the "if break|continue ;" that follows "dealloc ; alloc ;" - if (!printExperimental && Token::Match(tok2, "dealloc ; alloc ; if break|continue ;")) { - tok2->tokAt(3)->deleteNext(2); - done = false; - } - - // if break ; break ; => break ; - if (Token::Match(tok2->previous(), "[;{}] if break ; break ;")) { - tok2->deleteNext(3); - done = false; - } - - // Reduce "do { dealloc ; alloc ; } while(var) ;" => ";" - if (Token::simpleMatch(tok2->next(), "do { dealloc ; alloc ; } while(var) ;")) { - tok2->deleteNext(8); - done = false; - } - - // Ticket #7745 - // Delete "if (!var) { alloc ; dealloc }" blocks - if (Token::simpleMatch(tok2->next(), "if(!var) { alloc ; dealloc ; }")) { - tok2->deleteNext(7); - done = false; - } - - // Reduce "do { alloc ; } " => "alloc ;" - /** @todo If the loop "do { alloc ; }" can be executed twice, reduce it to "loop alloc ;" */ - if (Token::simpleMatch(tok2->next(), "do { alloc ; }")) { - tok2->deleteNext(2); - tok2->tokAt(2)->deleteNext(); - done = false; - } - - // Reduce "loop break ; => ";" - if (Token::Match(tok2->next(), "loop break|continue ;")) { - tok2->deleteNext(2); - done = false; - } - - // Reduce "loop|do ;" => ";" - if (Token::Match(tok2, "loop|do ;")) { - tok2->deleteThis(); - done = false; - } - - // Reduce "loop if break|continue ; !!else" => ";" - if (Token::Match(tok2->next(), "loop if break|continue ; !!else")) { - tok2->deleteNext(3); - done = false; - } - - // Reduce "loop { if break|continue ; !!else" => "loop {" - if (Token::Match(tok2, "loop { if break|continue ; !!else")) { - tok2->next()->deleteNext(3); - done = false; - } - - // Replace "do ; loop ;" with ";" - if (Token::simpleMatch(tok2, "; loop ;")) { - tok2->deleteNext(2); - done = false; - } - - // Replace "loop loop .." with "loop .." - if (Token::simpleMatch(tok2, "loop loop")) { - tok2->deleteThis(); - done = false; - } - - // Replace "loop if return ;" with "if return ;" - if (Token::simpleMatch(tok2->next(), "loop if return")) { - tok2->deleteNext(); - done = false; - } - - // Reduce "loop|while1 { dealloc ; alloc ; }" - if (Token::Match(tok2, "loop|while1 { dealloc ; alloc ; }")) { - // delete "{" - tok2->deleteNext(); - // delete "loop|while1" - tok2->deleteThis(); - - // delete "}" - tok2->tokAt(3)->deleteNext(); - - done = false; - } - - // loop { use ; callfunc ; } => use ; - // assume that the "callfunc" is not noreturn - if (Token::simpleMatch(tok2, "loop { use ; callfunc ; }")) { - tok2->deleteNext(6); - tok2->str("use"); - tok2->insertToken(";"); - done = false; - } - - // Delete if block in "alloc ; if(!var) return ;" - if (Token::simpleMatch(tok2, "alloc ; if(!var) return ;")) { - tok2->deleteNext(3); - done = false; - } - - // Reduce "[;{}] return use ; %name%" => "[;{}] return use ;" - if (Token::Match(tok2, "[;{}] return use ; %name%")) { - tok2->tokAt(3)->deleteNext(); - done = false; - } - - // Reduce "if(var) return use ;" => "return use ;" - if (Token::Match(tok2->next(), "if(var) return use ; !!else")) { - tok2->deleteNext(); - done = false; - } - - // malloc - realloc => alloc ; dealloc ; alloc ; - // Reduce "[;{}] alloc ; dealloc ; alloc ;" => "[;{}] alloc ;" - if (Token::Match(tok2, "[;{}] alloc ; dealloc ; alloc ;")) { - tok2->deleteNext(4); - done = false; - } - - // use; dealloc; => dealloc; - if (Token::Match(tok2, "[;{}] use ; dealloc ;")) { - tok2->deleteNext(2); - done = false; - } - - // use use => use - while (Token::simpleMatch(tok2, "use use")) { - tok2->deleteNext(); - done = false; - } - - // use use_ => use - if (Token::simpleMatch(tok2, "use use_")) { - tok2->deleteNext(); - done = false; - } - - // use_ use => use - if (Token::simpleMatch(tok2, "use_ use")) { - tok2->deleteThis(); - done = false; - } - - // use & use => use - while (Token::simpleMatch(tok2, "use & use")) { - tok2->deleteNext(2); - done = false; - } - - // & use use => use - while (Token::simpleMatch(tok2, "& use use")) { - tok2->deleteThis(); - tok2->deleteThis(); - done = false; - } - - // use; if| use; => use; - while (Token::Match(tok2, "[;{}] use ; if| use ;")) { - Token *t = tok2->tokAt(2); - t->deleteNext(2+(t->str()=="if" ? 1 : 0)); - done = false; - } - - // Delete first part in "use ; return use ;" - if (Token::Match(tok2, "[;{}] use ; return use ;")) { - tok2->deleteNext(2); - done = false; - } - - // try/catch - if (Token::simpleMatch(tok2, "try ; catch exit ;")) { - tok2->deleteNext(3); - tok2->deleteThis(); - done = false; - } - - // Delete second case in "case ; case ;" - while (Token::simpleMatch(tok2, "case ; case ;")) { - tok2->deleteNext(2); - done = false; - } - - // Replace switch with if (if not complicated) - if (Token::simpleMatch(tok2, "switch {")) { - // Right now, I just handle if there are a few case and perhaps a default. - bool valid = false; - bool incase = false; - for (const Token * _tok = tok2->tokAt(2); _tok; _tok = _tok->next()) { - if (_tok->str() == "{") - break; - - else if (_tok->str() == "}") { - valid = true; - break; - } - - else if (_tok->str() == "switch") - break; - - else if (_tok->str() == "loop") - break; - - else if (incase && _tok->str() == "case") - break; - - else if (Token::Match(_tok, "return !!;")) - break; - - if (Token::Match(_tok, "if return|break use| ;")) - _tok = _tok->tokAt(2); - - incase = incase || (_tok->str() == "case"); - incase = incase && (_tok->str() != "break" && _tok->str() != "return"); - } - - if (!incase && valid) { - done = false; - tok2->str(";"); - tok2->deleteNext(); - tok2 = tok2->next(); - bool first = true; - while (Token::Match(tok2, "case|default")) { - const bool def(tok2->str() == "default"); - tok2->str(first ? "if" : "}"); - if (first) { - first = false; - tok2->insertToken("{"); - } else { - // Insert "else [if] { - tok2->insertToken("{"); - if (! def) - tok2->insertToken("if"); - tok2->insertToken("else"); - tok2 = tok2->next(); - } - while (tok2) { - if (tok2->str() == "}") - break; - if (Token::Match(tok2, "break|return ;")) - break; - if (Token::Match(tok2, "if return|break use| ;")) - tok2 = tok2->tokAt(2); - else - tok2 = tok2->next(); - } - if (Token::simpleMatch(tok2, "break ;")) { - tok2->str(";"); - tok2 = tok2->tokAt(2); - } else if (tok2 && tok2->str() == "return") { - tok2 = tok2->tokAt(2); - } - } - } - } - } - - // If "--all" is given, remove all "callfunc".. - if (done && printExperimental) { - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (tok2->str() == "callfunc") { - tok2->deleteThis(); - done = false; - } - } - } - } -} - - - -const Token *CheckMemoryLeakInFunction::findleak(const Token *tokens) -{ - const Token *result; - - if (Token::Match(tokens, "alloc ; if|if(var)|ifv break|continue|return ;")) { - return tokens->tokAt(3); - } - - if ((result = Token::findsimplematch(tokens, "loop alloc ;")) != nullptr) { - return result; - } - - if ((result = Token::findmatch(tokens, "alloc ; if|if(var)|ifv return ;")) != nullptr) { - return result->tokAt(3); - } - - if ((result = Token::findmatch(tokens, "alloc ; alloc|assign|return callfunc| ;")) != nullptr) { - return result->tokAt(2); - } - - if ((result = Token::findmatch(tokens, "alloc ; loop|while1 {| alloc ;")) != nullptr) { - return result->tokAt(3 + (result->strAt(3) == "{")); - } - - if ((result = Token::findsimplematch(tokens, "; alloc ; if assign ;")) != nullptr) { - return result->tokAt(4); - } - - if (((result = Token::findsimplematch(tokens, "; alloc ; if dealloc ; }")) != nullptr) || - ((result = Token::findsimplematch(tokens, "; alloc ; if dealloc ; return ;")) != nullptr)) { - return result->tokAt(6); - } - - if ((result = Token::findsimplematch(tokens, "alloc ; }")) != nullptr) { - if (result->tokAt(3) == nullptr) - return result->tokAt(2); - } - - // No deallocation / usage => report leak at the last token - if (!Token::findmatch(tokens, "dealloc|use")) { - const Token *last = tokens; - while (last->next()) - last = last->next(); - - // not a leak if exit is called before the end of the function - if (!Token::Match(last->tokAt(-2), "exit|callfunc ; }")) - return last; - } - - return nullptr; -} - - -// Check for memory leaks for a function variable. -void CheckMemoryLeakInFunction::checkScope(const Token *startTok, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz) -{ - const std::list callstack; - - AllocType alloctype = No; - AllocType dealloctype = No; - - const Token *result; - - Token *tok = getcode(startTok, callstack, varid, alloctype, dealloctype, classmember, sz); - //tok->printOut((std::string("Checkmemoryleak: getcode result for: ") + varname).c_str()); - - const bool use_addr = bool(Token::findsimplematch(tok, "&use") != nullptr); - - // Simplify the code and check if freed memory is used.. - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - while (Token::Match(tok2, "[;{}] ;")) - tok2->deleteNext(); - } - if ((result = Token::findmatch(tok, "[;{}] dealloc ; use_ ;")) != nullptr) { - // TODO : Write a separate checker for this that uses valueFlowForward. - deallocuseError(result->tokAt(3), varname); - } - - // Replace "&use" with "use". Replace "use_" with ";" - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { - if (tok2->str() == "&use") - tok2->str("use"); - else if (tok2->str() == "use_") - tok2->str(";"); - else if (Token::simpleMatch(tok2, "loop use_ {")) - tok2->deleteNext(); - else if (tok2->str() == "::use") // Some kind of member function usage. Not analyzed very well. - tok2->str("use"); - else if (tok2->str() == "recursive") - tok2->str("use"); - else if (tok2->str() == "dealloc_") - tok2->str("dealloc"); - else if (tok2->str() == "realloc") { - tok2->str("dealloc"); - tok2->insertToken("alloc"); - tok2->insertToken(";"); - } - } - - // If the variable is not allocated at all => no memory leak - if (Token::findsimplematch(tok, "alloc") == nullptr) { - TokenList::deleteTokens(tok); - return; - } - - simplifycode(tok); - - if (mSettings->debugwarnings) { - tok->printOut(("Checkmemoryleak: simplifycode result for: " + varname).c_str()); - } - - // If the variable is not allocated at all => no memory leak - if (Token::findsimplematch(tok, "alloc") == nullptr) { - TokenList::deleteTokens(tok); - return; - } - - /** @todo handle "goto" */ - if (Token::findsimplematch(tok, "goto")) { - TokenList::deleteTokens(tok); - return; - } - - if ((result = findleak(tok)) != nullptr) { - memoryLeak(result, varname, alloctype); - } - - else if (!use_addr && (result = Token::findsimplematch(tok, "dealloc ; dealloc ;")) != nullptr) { - deallocDeallocError(result->tokAt(2), varname); - } - - // detect cases that "simplifycode" don't handle well.. - else if (tok && mSettings->debugwarnings) { - Token *first = tok; - while (first && first->str() == ";") - first = first->next(); - - bool noerr = false; - noerr = noerr || Token::simpleMatch(first, "alloc ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; dealloc ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; return use ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; use ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; use ; return ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; dealloc ; return ; }"); - noerr = noerr || Token::simpleMatch(first, "if alloc ; dealloc ; }"); - noerr = noerr || Token::simpleMatch(first, "if alloc ; return use ; }"); - noerr = noerr || Token::simpleMatch(first, "if alloc ; use ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; ifv return ; dealloc ; }"); - noerr = noerr || Token::simpleMatch(first, "alloc ; if return ; dealloc; }"); - - // Unhandled case.. - if (!noerr) - reportError(first, Severity::debug, "debug", - "inconclusive leak of " + varname + ": " + tok->stringifyList(false, false, false, false, false, nullptr, nullptr)); - } - - TokenList::deleteTokens(tok); -} -//--------------------------------------------------------------------------- - //--------------------------------------------------------------------------- // Check for memory leaks due to improper realloc() usage. @@ -2199,46 +585,6 @@ static bool isInMemberFunc(const Scope* scope) return (scope->functionOf != nullptr); } -void CheckMemoryLeakInFunction::check() -{ - const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); - - // Check locking/unlocking of global resources.. - for (const Scope * scope : symbolDatabase->functionScopes) { - if (!scope->hasInlineOrLambdaFunction()) - checkScope(scope->bodyStart->next(), emptyString, 0, scope->functionOf != nullptr, 1); - } - - // Check variables.. - for (const Variable* var : symbolDatabase->variableList()) { - if (!var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || !var->scope()) - continue; - - if (var->isReference()) - continue; - - if (!var->isPointer() && var->typeStartToken()->str() != "int") - continue; - - // check for known class without implementation (forward declaration) - if (var->isPointer() && var->type() && !var->typeScope()) - continue; - - if (var->scope()->hasInlineOrLambdaFunction()) - continue; - - unsigned int sz = mTokenizer->sizeOfType(var->typeStartToken()); - if (sz < 1) - sz = 1; - - if (var->isArgument()) - checkScope(var->scope()->bodyStart->next(), var->name(), var->declarationId(), isInMemberFunc(var->scope()), sz); - else - checkScope(var->nameToken(), var->name(), var->declarationId(), isInMemberFunc(var->scope()), sz); - } -} -//--------------------------------------------------------------------------- - //--------------------------------------------------------------------------- // Checks for memory leaks in classes.. diff --git a/lib/checkmemoryleak.h b/lib/checkmemoryleak.h index e84601238..be78bc475 100644 --- a/lib/checkmemoryleak.h +++ b/lib/checkmemoryleak.h @@ -187,95 +187,16 @@ public: void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override { CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.checkReallocUsage(); - // Commented out so we can evaluate if this checking can be removed. - //checkMemoryLeak.check(); } /** @brief Unit testing : testing the white list */ static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp); - /** @brief Perform checking */ - void check(); - /** * Checking for a memory leak caused by improper realloc usage. */ void checkReallocUsage(); - /** - * Inspect a function call. the call_func and getcode are recursive - * @param tok token where the function call occurs - * @param callstack callstack - * @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 - * - "recursive" : recursive function - * - "alloc" : the function returns allocated memory - * - "dealloc" : the function deallocates the variable - * - "dealloc_" - * - "use" : the variable is used (unknown usage of the variable => the checking bails out) - * - "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, bool &allocpar, unsigned int sz); - - /** - * Extract a new tokens list that is easier to parse than the "mTokenizer->tokens()", the - * extracted tokens list describes how the given variable is used. - * The getcode and call_func are recursive - * @param tok start parse token - * @param callstack callstack - * @param varid variable id - * @param alloctype keep track of what type of allocation is used - * @param dealloctype keeps track of what type of deallocation is used - * @param classmember should be set if the inspected function is a class member - * @param sz size of type, used to check for mismatching size of allocation. for example "int *a;" => the sz is "sizeof(int)" - * @return Newly allocated token array. Caller needs to release reserved - * memory by calling TokenList::deleteTokens(returnValue); - * Returned tokens: - * - "alloc" : the variable is allocated - * - "assign" : the variable is assigned a new value - * - "break" : corresponds to "break" - * - "callfunc" : a function call with unknown side effects - * - "continue" : corresponds to "continue" - * - "dealloc" : the variable is deallocated - * - "goto" : corresponds to a "goto" - * - "if" : there is an "if" - * - "if(var)" : corresponds with "if ( var != 0 )" - * - "if(!var)" : corresponds with "if ( var == 0 )" - * - "ifv" : the variable is used in some way in a "if" - * - "loop" : corresponds to either a "for" or a "while" - * - "realloc" : the variable is reallocated - * - "return" : corresponds to a "return" - * - "use" : unknown usage -> bail out checking of this execution path - * - "&use" : the address of the variable is taken - * - "::use" : calling member function of class - * - "use_" : content of variable is accessed (used to warn access after dealloc) - */ - Token *getcode(const Token *tok, std::list callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, unsigned int sz); - - /** - * Simplify code e.g. by replacing empty "{ }" with ";" - * @param tok first token. The tokens list can be modified. - */ - void simplifycode(Token *tok) const; - - static const Token *findleak(const Token *tokens); - - /** - * Checking the variable varname - * @param startTok start token - * @param varname name of variable (for error messages) - * @param varid variable id - * @param classmember is the scope inside a class member function - * @param sz size of type.. if the variable is a "int *" then sz should be "sizeof(int)" - */ - void checkScope(const Token *startTok, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz); - private: /** Report all possible errors (for the --errorlist) */ void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 68c59c676..386f950d6 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -149,7 +149,6 @@ private: // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this); checkMemoryLeak.checkReallocUsage(); - checkMemoryLeak.check(); } @@ -158,18 +157,7 @@ private: LOAD_LIB_2(settings1.library, "posix.cfg"); LOAD_LIB_2(settings2.library, "std.cfg"); - // Check that getcode works correctly.. - TEST_CASE(testgetcode); - - // check that call_func works correctly.. - TEST_CASE(call_func); - - // Check that simplifycode works correctly.. - TEST_CASE(simplifycode); - - // Check that errors are found.. - TEST_CASE(findleak); - +/* TEST_CASE(simple5); TEST_CASE(simple7); TEST_CASE(simple9); // Bug 2435468 - member function "free" @@ -277,7 +265,6 @@ private: TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); - TEST_CASE(realloc6); TEST_CASE(realloc7); TEST_CASE(realloc8); TEST_CASE(realloc9); @@ -382,551 +369,7 @@ private: TEST_CASE(crash); TEST_CASE(trac7680); TEST_CASE(trac7440); - } - - std::string getcode(const char code[], const char varname[], bool classfunc=false) { - // Clear the error buffer.. - errout.str(""); - - settings2.standards.posix = true; - - // Tokenize.. - Tokenizer tokenizer(&settings2, this); - std::istringstream istr(code); - if (!tokenizer.tokenize(istr, "test.cpp")) - return ""; - tokenizer.simplifyTokenList2(); - const Token * start = tokenizer.tokens(); - const SymbolDatabase * db = tokenizer.getSymbolDatabase(); - if (db && db->functionScopes.size()) - start = db->functionScopes[0]->bodyStart->next(); - - const unsigned int varId(Token::findmatch(start, varname)->varId()); - - // getcode.. - CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings2, nullptr); - std::list callstack(1, nullptr); - CheckMemoryLeak::AllocType allocType, deallocType; - allocType = deallocType = CheckMemoryLeak::No; - Token *tokens = checkMemoryLeak.getcode(start, callstack, varId, allocType, deallocType, classfunc, 1); - - // stringify.. - std::ostringstream ret; - for (const Token *tok = tokens; tok; tok = tok->next()) - ret << tok->str(); - - TokenList::deleteTokens(tokens); - - return ret.str(); - } - - - - - void testgetcode() { - // alloc; - ASSERT_EQUALS(";;alloc;", getcode("int *a = malloc(100);", "a")); - TODO_ASSERT_EQUALS(";;alloc;", ";;alloccallfunc;", getcode("int *a = ::malloc(100);", "a")); - ASSERT_EQUALS(";;alloc;", getcode("int *a = new int;", "a")); - ASSERT_EQUALS(";;alloc;", getcode("int *a = new int[10];", "a")); - ASSERT_EQUALS(";;alloc;", getcode("int **a = new int*[10];", "a")); - ASSERT_EQUALS(";;alloc;", getcode("int * const a = new int[10];", "a")); - ASSERT_EQUALS(";;alloc;", getcode("const int * const a = new int[10];", "a")); - ASSERT_EQUALS(";;assign;", getcode("A * a = new (X) A;", "a")); - ASSERT_EQUALS(";;alloc;", getcode("int i = open(a,b);", "i")); - ASSERT_EQUALS(";;assign;", getcode("int i = open();", "i")); - ASSERT_EQUALS(";;alloc;use;", getcode("int *p; dostuff(p = new int);", "p")); - ASSERT_EQUALS(";;alloc;use;", getcode("int *p; dostuff(p = new int());", "p")); - ASSERT_EQUALS(";;alloc;use;", getcode("int *p; fred.dostuff(p = new int);", "p")); - ASSERT_EQUALS(";;alloc;use;", getcode("int *p; fred.dostuff(p = new int());", "p")); - - // alloc; return use; - ASSERT_EQUALS(";;alloc;returnuse;", getcode("int *a = new int[10]; return a;", "a")); - ASSERT_EQUALS(";;alloc;returnuse;", getcode("char *a = new char[100]; return (char *)a;", "a")); - - // alloc; return; - ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return 0;", "s")); - ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return s[0];", "s")); - ASSERT_EQUALS(";;alloc;return;", getcode("char *s = new char[100]; return strcmp(s,x);", "s")); - - // lock/unlock.. - ASSERT_EQUALS(";;alloc;", getcode("int a; __cppcheck_lock();", "")); - ASSERT_EQUALS(";;callfunc;", getcode("int a; __cppcheck_lock();", "a")); - ASSERT_EQUALS(";;dealloc;", getcode("int a; __cppcheck_unlock();", "")); - ASSERT_EQUALS(";;callfunc;", getcode("int a; __cppcheck_unlock();", "a")); - - // dealloc; - ASSERT_EQUALS(";;dealloc;", getcode("char *s; free(s);", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; free((void *)s);", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; free((void *)(s));", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; free(reinterpret_cast(s));", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; ::free(s);", "s")); // #2802 - ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete s;", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete (s);", "s")); - TODO_ASSERT_EQUALS(";;dealloc;", - ";;;", getcode("char *s; delete (void *)(s);", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete [] s;", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; delete [] (s);", "s")); - ASSERT_EQUALS(";;dealloc;", getcode("void *p; foo(fclose(p));", "p")); - ASSERT_EQUALS(";;dealloc;", getcode("void *p; foo(close(p));", "p")); - ASSERT_EQUALS(";;;;", getcode("FILE *f1; FILE *f2; fclose(f1);", "f2")); - ASSERT_EQUALS(";;returnuse;", getcode("FILE *f; return fclose(f) == EOF ? 1 : 2;", "f")); - ASSERT_EQUALS(";;dealloc;", getcode("char *s; s ? free(s) : 0;", "s")); - - // if.. - ASSERT_EQUALS(";;if{}", getcode("char *s; if (a) { }", "s")); - ASSERT_EQUALS(";;dealloc;ifv{}", getcode("FILE *f; if (fclose(f)) { }", "f")); - ASSERT_EQUALS(";;if(!var){}else{}", getcode("char *s; if (!s) { } else { }", "s")); - TODO_ASSERT_EQUALS(";;ifv{}",";;if{}", getcode("char *s; if (a && s) { }", "s")); - ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (s && a) { }", "s")); - ASSERT_EQUALS(";;;ifv{}", getcode("char *s; int a; if (a && s) { }", "s")); - ASSERT_EQUALS(";;;ifv{}", getcode("char *s; int a; if (s && a) { }", "s")); - ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (a || s) { }", "s")); - ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (s || a) { }", "s")); - ASSERT_EQUALS(";;if(!var){}", getcode("char *s; if (a && !s) { }", "s")); - ASSERT_EQUALS(";;ifv{}", getcode("char *s; if (foo(!s)) { }", "s")); - ASSERT_EQUALS(";;;if{dealloc;};if{dealloc;return;}assign;returnuse;", getcode("char *buf, *tmp; tmp = realloc(buf, 40); if (!(tmp)) { free(buf); return; } buf = tmp; return buf;", "buf")); - ASSERT_EQUALS(";;if{}", getcode("FILE *f; if (fgets(buf,100,f)){}", "f")); - ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int fd = open(a,b); if (0 < fd) { close(fd); }", "fd")); - ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (x(s)) { }", "s")); - ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (x(&s)) { }", "s")); - ASSERT_EQUALS(";;use;if{}", getcode("char *s; if (!s || x(&s)) { }", "s")); - ASSERT_EQUALS(";;ifv{}", getcode("int ffd; if (ffd<0 && (ffd=a)<0){}", "ffd")); - - // if (ticket #2442) - ASSERT_EQUALS(";;;;if(!var){;}ifv{}", getcode("char *s; int x = 0; if (!s) { x = 2; } if (x) { }", "s")); - ASSERT_EQUALS(";;;;if(!var){;}if{}", getcode("char *s; int x = 0; if (!s) { x = 2; } if (y) { }", "s")); - - // switch.. - ASSERT_EQUALS(";;switch{case;;break;};", getcode("char *s; switch(a){case 1: break;};", "s")); - - // loop.. - ASSERT_EQUALS(";;loop{}", getcode("char *s; while (a) { }", "s")); - ASSERT_EQUALS(";;loopcallfunc{}", getcode("char *s; while (a()) { }", "s")); - ASSERT_EQUALS(";;loop{}", getcode("char *s; for (a;b;c) { }", "s")); - ASSERT_EQUALS(";;loop{alloc;}", getcode("char *s; for (a;b;c) { s=malloc(10); }", "s")); - ASSERT_EQUALS(";;do{}loop;", getcode("char *s; do { } while (a);", "s")); - ASSERT_EQUALS(";;while1{}", getcode("char *s; while(true) { }", "s")); - ASSERT_EQUALS(";;while1{}", getcode("char *s; for(;;) { }", "s")); - ASSERT_EQUALS(";;while(var){}", getcode("char *s; while (s) { }", "s")); - ASSERT_EQUALS(";;while(!var){}", getcode("char *s; while (!s) { }", "s")); - ASSERT_EQUALS(";;alloc;while(var){}", getcode("int fd = open(a,b); while (fd >= 0) { }", "fd")); - ASSERT_EQUALS(";;alloc;while(!var){}", getcode("int fd = open(a,b); while (fd < 0) { }", "fd")); - - // asprintf.. - ASSERT_EQUALS(";;alloc;", getcode("char *s; asprintf(&s, \"xyz\");", "s")); - ASSERT_EQUALS(";;alloc;", getcode("char *s; asprintf(&s, \"s: %s\", s);", "s")); - ASSERT_EQUALS(";;;", getcode("char *s; asprintf(&p, \"s: %s\", s);", "s")); - - // Since we don't check how the return value is used we must bail out - ASSERT_EQUALS("", getcode("char *s; int ret = asprintf(&s, \"xyz\");", "s")); - TODO_ASSERT_EQUALS(";;alloc;", - "", getcode("char *s; int ret; ret=asprintf(&s, \"xyz\"); if (ret==-1) return;", "s")); - - // use.. - ASSERT_EQUALS(";;use;", getcode("char *s; a(s);", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; (*a)(s);", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; abc.a(s);", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; s2 = s;", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; s2 = s + 10;", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; s2 = x + s;", "s")); - ASSERT_EQUALS(";;use;if{;}", getcode("char *s; if (foo(s)) ;", "s")); - ASSERT_EQUALS(";;use;", getcode("char *s; map1[s] = 0;", "s")); - ASSERT_EQUALS(";;;use;", getcode("char *p; const char *q; q = p;", "p")); - ASSERT_EQUALS(";;use;;", getcode("char *s; x = {1,s};", "s")); - ASSERT_EQUALS(";{};;alloc;;use;", getcode("struct Foo { }; Foo *p; p = malloc(10); const Foo *q; q = p;", "p")); - ASSERT_EQUALS(";;useuse_;", getcode("struct AB *ab; f(ab->a);", "ab")); - ASSERT_EQUALS(";;use;", getcode("struct AB *ab; ab = pop(ab);", "ab")); - - // non-use.. - ASSERT_EQUALS(";;use_;", getcode("char *s; c = x + s[0];","s")); - ASSERT_EQUALS(";;use_;", getcode("char *s; c = s[0] + x;","s")); - ASSERT_EQUALS(";;use_;", getcode("type *c; y = x + c->y;","c")); - ASSERT_EQUALS(";;use_;", getcode("type *c; y = c->y + x;","c")); - ASSERT_EQUALS(";;use_;", getcode("char *s; s = s + 1;", "s")); - ASSERT_EQUALS(";;dealloc;;", getcode("struct foo *s; free(s); printf(a,sizeof(*s));", "s")); - ASSERT_EQUALS(";;do{dealloc;;}while(var);", getcode("struct foo *s; do{free(s); printf(a,sizeof(*s));}while(s);", "s")); - - // use reference - ASSERT_EQUALS(";;callfunc&use;", getcode("struct AB *ab; f(&ab);", "ab")); - - // return.. - ASSERT_EQUALS(";;return;", getcode("char *s; return;", "s")); - ASSERT_EQUALS(";;returnuse;", getcode("char *s; return s;", "s")); - ASSERT_EQUALS(";;return;", getcode("char *s; return 5 + s[0];", "s")); - - // assign.. - ASSERT_EQUALS(";;assign;", getcode("char *s; s = 0;", "s")); - ASSERT_EQUALS(";;;", getcode("char *s; s = strcpy(s, p);", "s")); - - // callfunc.. - ASSERT_EQUALS(";;assign;", getcode("char *s; s = a();", "s")); - ASSERT_EQUALS(";;callfunc;", getcode("char *s; a();", "s")); - ASSERT_EQUALS(";;callfunc;", getcode("char *s; abc.a();", "s")); - ASSERT_EQUALS(";;;", getcode("char *s; x = a();", "s")); // the function call is irrelevant - - // exit.. - ASSERT_EQUALS(";;exit;", getcode("char *s; exit(0);", "s")); - ASSERT_EQUALS(";;callfunc;", getcode("char *s; _exit(0);", "s")); // not in std.cfg - ASSERT_EQUALS(";;exit;", getcode("char *s; abort();", "s")); - ASSERT_EQUALS(";;callfunc;", getcode("char *s; err(0);", "s")); // not in std.cfg - ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { exit(0); }", "s")); - ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { ::exit(0); }", "s")); - ASSERT_EQUALS(";;if{exit;}", getcode("char *s; if (a) { std::exit(0); }", "s")); - - // list_for_each - ASSERT_EQUALS(";;exit;{}}", getcode("void f() { char *s; list_for_each(x,y,s) { } }", "s")); - - // open/close - ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int f; f=open(a,b); if(f>=0)close(f);", "f")); - ASSERT_EQUALS(";;alloc;if(var){dealloc;}", getcode("int f; f=open(a,b); if(f>-1)close(f);", "f")); - ASSERT_EQUALS(";;alloc;ifv{;}", getcode("int f; f=open(a,b); if(f!=-1 || x);", "f")); - ASSERT_EQUALS(";;;dealloc;loop{}", getcode(";int f; while (close(f) == -1) { }", "f")); - ASSERT_EQUALS(";;;dealloc;assign;;", getcode(";int res; res = close(res);", "res")); - - ASSERT_EQUALS(";;dealloc;", getcode("int f; e |= fclose(f);", "f")); - ASSERT_EQUALS(";;dealloc;", getcode("int f; e += fclose(f);", "f")); - ASSERT_EQUALS(";;dealloc;", getcode("int f; foo(fclose(f));", "f")); - - // fcloseall.. - ASSERT_EQUALS(";;alloc;;", getcode("char *s; s = malloc(10); fcloseall();", "s")); - ASSERT_EQUALS(";;alloc;dealloc;", getcode("FILE *f; f = fopen(a,b); fcloseall();", "f")); - - // call memcpy in class function.. - ASSERT_EQUALS(";;alloc;;", getcode("char *s; s = new char[10]; memcpy(s,a);", "s", true)); - - // #2112 - Segmentation fault in the getcode function - ASSERT_THROW(getcode("page *one = foo();\n" - "ASSERT(one, return 0)\n" - "const int two = rand();\n" - "return 0;\n" - "}", "one"), InternalError); - - // ticket #2336: calling member function with same name as a white_list function - ASSERT_EQUALS(";;use;", getcode("char *s; foo.write(s);", "s")); - - // #2473 - inner struct - ASSERT_EQUALS(";;alloc;{;;};dealloc;", - getcode("char *s = new char[10];\n" - "struct ab { int a, b; };\n" - "delete [] s;\n", "s")); - - // #4405 - catch - ASSERT_EQUALS(";;catch{}", getcode("char *s; catch(err) { }", "s")); - } - - bool test_white_list(const std::string& str, bool cpp = true) const { - return CheckMemoryLeakInFunction::test_white_list(str, &settings1, cpp); - } - - void call_func() const { - // whitelist.. - ASSERT_EQUALS(true, test_white_list("qsort")); - ASSERT_EQUALS(true, test_white_list("scanf")); - ASSERT_EQUALS(true, test_white_list("sscanf")); - - // #1293 - ASSERT_EQUALS(true, test_white_list("time")); - ASSERT_EQUALS(true, test_white_list("asctime")); - ASSERT_EQUALS(true, test_white_list("asctime_r")); - ASSERT_EQUALS(true, test_white_list("ctime")); - ASSERT_EQUALS(true, test_white_list("ctime_r")); - ASSERT_EQUALS(true, test_white_list("gmtime")); - ASSERT_EQUALS(true, test_white_list("gmtime_r")); - ASSERT_EQUALS(true, test_white_list("localtime")); - ASSERT_EQUALS(true, test_white_list("localtime_r")); - ASSERT_EQUALS(true, test_white_list("memcmp")); - ASSERT_EQUALS(true, test_white_list("gets")); - ASSERT_EQUALS(true, test_white_list("vprintf")); - ASSERT_EQUALS(true, test_white_list("vfprintf")); - ASSERT_EQUALS(true, test_white_list("vsprintf")); - ASSERT_EQUALS(true, test_white_list("snprintf")); - ASSERT_EQUALS(true, test_white_list("vsnprintf")); - - ASSERT_EQUALS(true, test_white_list("delete", true)); - ASSERT_EQUALS(false, test_white_list("delete", false)); - - static const char * const call_func_white_list[] = { - "access", "asprintf", "atof", "atoi", "atol", "chdir", "chmod", "clearerr", "chown" - , "fchmod", "fcntl", "fdatasync", "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets" - , "flock", "for", "fprintf", "fputc", "fputs", "fread", "free", "freopen", "fscanf", "fseek" - , "fseeko", "fsetpos", "fstat", "fsync", "ftell", "ftello", "ftruncate" - , "fwrite", "getc", "if", "ioctl", "lockf", "lseek", "open", "memchr", "memcpy" - , "memmove", "memset", "mkstemp", "perror", "posix_fadvise", "posix_fallocate", "pread" - , "printf", "puts", "pwrite", "read", "readahead", "readdir", "readdir_r", "readv" - , "realloc", "return", "rewind", "rewinddir", "scandir", "seekdir" - , "setbuf", "setbuffer", "setlinebuf", "setvbuf", "snprintf", "sprintf", "stpcpy", "strcasecmp" - , "strcat", "strchr", "strcmp", "strcpy", "stricmp", "strlen", "strncat", "strncmp" - , "strncpy", "strrchr", "strspn","strstr", "strtod", "strtol", "strtoul", "switch" - , "sync_file_range", "telldir", "typeid", "while", "write", "writev", "lstat", "stat" - , "_open", "_wopen", "vscanf", "vsscanf", "vfscanf", "vasprintf", "utime", "utimes", "unlink" - , "tempnam", "system", "symlink", "strpbrk", "strncasecmp", "strdup", "strcspn", "strcoll" - , "setlocale", "sethostname", "rmdir", "rindex", "rename", "remove", "adjtime", "creat", "execle" - , "execl", "execlp", "execve", "execv", "fmemopen", "fnmatch", "fopencookie", "fopen" - , "getgrnam", "gethostbyaddr", "getnetbyname", "getopt", "getopt_long", "getprotobyname", "getpwnam" - , "getservbyname", "getservbyport", "glob", "index", "inet_addr", "inet_aton", "inet_network" - , "initgroups", "link", "mblen", "mbstowcs", "mbtowc", "mkdir", "mkfifo", "mknod", "obstack_printf" - , "obstack_vprintf", "opendir", "parse_printf_format", "pathconf", "popen", "psignal" - , "readlink", "regcomp", "strxfrm", "wordexp", "sizeof", "strtok" - }; - - for (unsigned int i = 0; i < (sizeof(call_func_white_list) / sizeof(char *)); ++i) { - bool ret = test_white_list(call_func_white_list[i]); - ASSERT_EQUALS("", ret ? "" : call_func_white_list[i]); - } - } - - - std::string simplifycode(const char code[]) { - // Clear the error buffer.. - errout.str(""); - - // Tokenize.. - std::istringstream istr(code); - Tokenizer tokenizer(&settings0, this); - tokenizer.list.createTokens(istr, "test.cpp"); - - // replace "if ( ! var )" => "if(!var)" - for (Token *tok = tokenizer.list.front(); tok; tok = tok->next()) { - if (Token::Match(tok, "if|while ( var )")) { - Token::eraseTokens(tok, tok->tokAt(4)); - tok->str(tok->str() + "(var)"); - } - - else if (Token::Match(tok, "if|while ( ! var )")) { - Token::eraseTokens(tok, tok->tokAt(5)); - tok->str(tok->str() + "(!var)"); - } - } - - CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings0, this); - checkMemoryLeak.simplifycode(tokenizer.list.front()); - - return tokenizer.tokens()->stringifyList(0, false); - } - - - // Test that the CheckMemoryLeaksInFunction::simplifycode works - void simplifycode() { - ASSERT_EQUALS(";", simplifycode("; ; ; ;")); - ASSERT_EQUALS(";", simplifycode("; if ;")); - ASSERT_EQUALS("alloc ;", simplifycode("alloc ; if ; if(var) ; ifv ; if(!var) ;")); - ASSERT_EQUALS("alloc ;", simplifycode("alloc ; if ; else ;")); - - // use.. - ASSERT_EQUALS("; use ; }", simplifycode("; use use ; }")); - ASSERT_EQUALS("; use ; }", simplifycode("; use use_ ; }")); - ASSERT_EQUALS("; use ; }", simplifycode("; use_ use ; }")); - ASSERT_EQUALS("; use ; }", simplifycode("; &use use ; }")); - ASSERT_EQUALS("; use ; }", simplifycode("; use &use ; }")); - ASSERT_EQUALS("; alloc ; dealloc ; }", simplifycode("; alloc ; use ; use ; if use ; dealloc ; }")); - - // if, else.. - ASSERT_EQUALS("; alloc ; if break ; dealloc ;", simplifycode("; alloc ; if { break; } dealloc ;")); - ASSERT_EQUALS("; alloc ; if continue ; dealloc ;", simplifycode("; alloc ; if { continue; } dealloc ;")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { return use; }")); - ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode("; alloc; if(!var) { return; } dealloc;")); - ASSERT_EQUALS("; alloc ;", simplifycode("; if { alloc; } else { return; }")); - ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode("; alloc ; if(!var) { alloc ; } dealloc ;")); - ASSERT_EQUALS("; use ;", simplifycode("; if(var) use ;")); - ASSERT_EQUALS("; break ;", simplifycode("; if break ; else break ;")); - ASSERT_EQUALS("; alloc ; if return ;", simplifycode("; alloc ; loop { if return ; if continue ; }")); - ASSERT_EQUALS("; alloc ; if return ;", simplifycode("; alloc ; loop { if continue ; else return ; }")); - - ASSERT_EQUALS("; alloc ; if dealloc ;", simplifycode("; alloc ; if(!var) { return ; } if { dealloc ; }")); - ASSERT_EQUALS("; if alloc ; else assign ; return use ;", simplifycode("; callfunc ; if callfunc { alloc ; } else { assign ; } return use ;")); - - ASSERT_EQUALS("; dealloc ; return ;", simplifycode("; while1 { if callfunc { dealloc ; return ; } else { continue ; } }")); - - // remove outer if (#2733) - ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if { if return use ; } return ; }")); - ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if { if(var) return use ; } return ; }")); - ASSERT_EQUALS("alloc ; return ; }", simplifycode("alloc ; if(var) { if return use ; } return ; }")); - - // "if ; .." - ASSERT_EQUALS("; if xxx ;", simplifycode("; if ; else xxx ;")); - ASSERT_EQUALS("; if(var) xxx ;", simplifycode("; if(!var) ; else xxx ;")); - ASSERT_EQUALS("; if(!var) xxx ;", simplifycode("; if(var) ; else xxx ;")); - ASSERT_EQUALS("; ifv xxx ;", simplifycode("; ifv ; else xxx ;")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { dealloc; return; }")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc; if { return use; }")); - ASSERT_EQUALS("; alloc ; return ;", simplifycode(";alloc;if{return;}return;")); - ASSERT_EQUALS("; alloc ; if assign ; dealloc ;", simplifycode(";alloc;if{assign;}dealloc;")); - - // if(var) - ASSERT_EQUALS("; alloc ; return use ;", simplifycode("; alloc ; return use ;")); - ASSERT_EQUALS("; alloc ; return use ;", simplifycode("; alloc ; ifv return ; return use ;")); - - // switch.. - ASSERT_EQUALS("; alloc ; dealloc ;", simplifycode(";alloc;switch{case;break;};dealloc;")); - ASSERT_EQUALS(";", simplifycode("; switch { case ; return ; default ; break ; }")); - ASSERT_EQUALS(";", simplifycode("; switch { case ; if { return ; } break ; default ; break ; }")); - ASSERT_EQUALS("; use ;", simplifycode("; switch { case ; return ; default ; use ; break ; }")); - ASSERT_EQUALS("; use ;", simplifycode("; while1 { loop { ; } switch { case ; dealloc ; return ; default ; break ; } }")); - ASSERT_EQUALS("; { dealloc ; return ; } }", simplifycode("switch { case ; case ; dealloc ; return ; default ; dealloc ; return ; } }")); - - // loops.. - ASSERT_EQUALS(";", simplifycode("; loop { ; }")); - ASSERT_EQUALS(";", simplifycode("; loop { break; }")); - ASSERT_EQUALS(";", simplifycode("; loop { if { break; } }")); - ASSERT_EQUALS("; loop alloc ;", simplifycode("; loop { alloc ; }")); - ASSERT_EQUALS("; alloc ; alloc ;", simplifycode("; alloc ; do { alloc ; } loop ;")); - ASSERT_EQUALS("; exit ;", simplifycode("; alloc ; do { } loop ; exit ;")); - ASSERT_EQUALS("; loop use ;", simplifycode("; loop { loop loop use ; } ;")); - ASSERT_EQUALS("; }", simplifycode("; loop { if break ; break ; } ; }")); - ASSERT_EQUALS("; }", simplifycode("; loop { if continue ; if continue ; } ; }")); - { - // ticket #3267 - const char expected[] = "; loop if alloc ; if { dealloc ; return ; } }"; - ASSERT_EQUALS(expected, simplifycode("; loop { if alloc ; } if { dealloc ; return ; } }")); - ASSERT_EQUALS(expected, simplifycode("; loop { if { alloc ; if(!var) { return ; } } } if { dealloc ; return ; } }")); - } - - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; while(!var) alloc ;")); - - ASSERT_EQUALS("; alloc ; dealloc ; return ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } }")); - ASSERT_EQUALS("; alloc ; dealloc ; return ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } if { continue ; } }")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; while1 { if { dealloc ; return ; } if { break ; } }")); - ASSERT_EQUALS("; alloc ; use ; }", simplifycode("; alloc ; while1 { if { dealloc ; return ; } continue ; } ; }")); - - ASSERT_EQUALS(";", simplifycode("; do { dealloc ; alloc ; } while(var) ;")); - ASSERT_EQUALS("dealloc ; alloc ;", simplifycode("loop { dealloc ; alloc ; }")); - ASSERT_EQUALS("dealloc ; alloc ;", simplifycode("while1 { dealloc ; alloc ; }")); - ASSERT_EQUALS("use ; }", simplifycode("loop { use ; callfunc ; } }")); - - ASSERT_EQUALS(";", simplifycode("; loop { if { continue ; } else { if continue ; } }")); - ASSERT_EQUALS(";", simplifycode("; loop { { if continue ; if continue ; } }")); - - ASSERT_EQUALS("; use ;", simplifycode("; while1 { if { dealloc ; return ; } if { if { continue ; } } }")); - - // scope.. - TODO_ASSERT_EQUALS("; assign ; if alloc ; }", - "; assign ; dealloc ; if alloc ; }", simplifycode("; assign ; { dealloc ; if alloc ; } }")); - - // callfunc.. - ASSERT_EQUALS("; callfunc ; }", simplifycode(";callfunc;}")); - ASSERT_EQUALS(";", simplifycode(";callfunc;;")); - ASSERT_EQUALS("; callfunc ; }", simplifycode(";callfunc callfunc ; }")); - ASSERT_EQUALS("dealloc ; alloc ; return ; }", simplifycode("while1 { dealloc ; alloc ; } callfunc ; return ; }")); - ASSERT_EQUALS("; }", simplifycode("loop callfunc ; }")); - ASSERT_EQUALS("alloc ; dealloc ; }", simplifycode("alloc ; if { dealloc ; callfunc } dealloc ; }")); // #4405 - - // #2900 - don't report false positive - ASSERT_EQUALS("; alloc ; if { if { dealloc ; callfunc ; } return ; } dealloc ; }", - simplifycode("; alloc ; if { if { dealloc ; callfunc ; } return ; } dealloc ; }")); - - // exit.. - ASSERT_EQUALS("; exit ;", simplifycode("; alloc; exit;")); - ASSERT_EQUALS("; exit ;", simplifycode("; alloc; if { loop ; } dealloc; exit;")); - ASSERT_EQUALS(";", simplifycode("; if { alloc; exit; }")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; if { use; exit; }")); - ASSERT_EQUALS("; alloc ;", simplifycode("; alloc ; if(!var) { exit; }")); - TODO_ASSERT_EQUALS(";", - "; if(var) exit ;", simplifycode("; alloc ; if(var) { exit; }")); - TODO_ASSERT_EQUALS(";\n; alloc ;", - "; alloc ; ifv exit ;", simplifycode("; alloc ; ifv { exit; }")); - - // try-catch - ASSERT_EQUALS("; }", simplifycode("; try ; catch exit ; }")); - - // dealloc; dealloc; - ASSERT_EQUALS("; alloc ; if dealloc ; dealloc ;", simplifycode("; alloc ; if { dealloc ; } dealloc ;")); - - // use ; dealloc ; - ASSERT_EQUALS("; alloc ; use ; if return ; dealloc ;", simplifycode("; alloc ; use ; if { return ; } dealloc ;")); - - // #2635 - false negative - ASSERT_EQUALS("; alloc ; return use ; }", - simplifycode("; alloc ; if(!var) { loop { ifv { } } alloc ; } return use; }")); - } - - - - // is there a leak in given code? if so, return the linenr - unsigned int dofindleak(const char code[]) { - // Clear the error buffer.. - errout.str(""); - - settings0.debugwarnings = true; - - // Tokenize.. - std::istringstream istr(code); - TokenList list(&settings0); - list.createTokens(istr,"test.cpp"); - Token *tokens=list.front(); - - // replace "if ( ! var )" => "if(!var)" - for (Token *tok = tokens; tok; tok = tok->next()) { - if (tok->str() == "if_var") { - tok->str("if(var)"); - } - - else if (Token::simpleMatch(tok, "if ( var )")) { - Token::eraseTokens(tok, tok->tokAt(4)); - tok->str("if(var)"); - } - - else if (Token::simpleMatch(tok, "if ( ! var )")) { - Token::eraseTokens(tok, tok->tokAt(5)); - tok->str("if(!var)"); - } - } - - const Token *tok = CheckMemoryLeakInFunction::findleak(tokens); - - settings0.debugwarnings = false; - - return (tok ? tok->linenr() : (unsigned int)(-1)); - } - - void findleak() { - static const unsigned int notfound = (unsigned int)(-1); - - ASSERT_EQUALS(1, dofindleak("alloc;")); - ASSERT_EQUALS(1, dofindleak("; use; { alloc; }")); - ASSERT_EQUALS(2, dofindleak("alloc;\n return;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; return use;")); - ASSERT_EQUALS(2, dofindleak("alloc;\n callfunc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; use;")); - ASSERT_EQUALS(notfound, dofindleak("assign; alloc; dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("assign; if alloc; dealloc;")); - - // if alloc.. - ASSERT_EQUALS(2, dofindleak("if alloc;\n return;")); - ASSERT_EQUALS(notfound, dofindleak("if alloc;\n return use;")); - ASSERT_EQUALS(notfound, dofindleak("if alloc;\n use;")); - ASSERT_EQUALS(notfound, dofindleak("if alloc;\n if assign;\n if dealloc; }")); - - // if.. - ASSERT_EQUALS(notfound, dofindleak("alloc; ifv dealloc;")); - ASSERT_EQUALS(2, dofindleak("alloc;\n if return;\n dealloc;")); - ASSERT_EQUALS(2, dofindleak("alloc;\n if continue;\n dealloc;")); - ASSERT_EQUALS(2, dofindleak("alloc;\n if_var return;\n dealloc;")); - ASSERT_EQUALS(3, dofindleak("alloc;\n if\n return;\n dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; return; } dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; return; } dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc ; alloc; } dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc;\n if(!var)\n { callfunc;\n return;\n }\n use;")); - - ASSERT_EQUALS(notfound, dofindleak("alloc; if { return use; } dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc; if { dealloc; return; } dealloc;")); - - ASSERT_EQUALS(5, dofindleak("{\n;\n alloc;\n if dealloc;\n}")); - - // assign.. - ASSERT_EQUALS(2, dofindleak("alloc;\n assign;\n dealloc;")); - ASSERT_EQUALS(notfound, dofindleak("alloc;\n if(!var) assign;\n dealloc;")); - ASSERT_EQUALS(2, dofindleak(";alloc;\n if assign;\n dealloc;")); - - // loop.. - TODO_ASSERT_EQUALS(1, notfound, dofindleak("; loop { alloc ; if break; dealloc ; }")); - TODO_ASSERT_EQUALS(1, notfound, dofindleak("; loop { alloc ; if continue; dealloc ; }")); - ASSERT_EQUALS(notfound, dofindleak("; loop { alloc ; if break; } dealloc ;")); - ASSERT_EQUALS(1, dofindleak("; loop alloc ;")); - ASSERT_EQUALS(1, dofindleak("; loop alloc ; dealloc ;")); - - // callfunc (might be noreturn) - ASSERT_EQUALS(notfound, dofindleak("; alloc ; callfunc ; }")); +*/ } @@ -2912,11 +2355,6 @@ private: ASSERT_EQUALS("", errout.str()); } - void realloc6() { - ASSERT_EQUALS(";;realloc;;", getcode("char *buf; buf=realloc(buf,100);", "buf")); - ASSERT_EQUALS(";;alloc;", getcode("char *buf; buf=realloc(0,100);", "buf")); - } - void realloc7() { check("bool foo(size_t nLen, char* pData)\n" "{\n" @@ -6079,7 +5517,6 @@ private: CheckMemoryLeakInClass checkMemoryLeak2(&tokenizer, &settings, this); CheckMemoryLeakStructMember checkMemoryLeak3(&tokenizer, &settings, this); CheckMemoryLeakNoVar checkMemoryLeak4(&tokenizer, &settings, this); - checkMemoryLeak1.check(); checkMemoryLeak1.checkReallocUsage(); checkMemoryLeak2.check(); checkMemoryLeak3.check(); @@ -6089,9 +5526,10 @@ private: void run() override { LOAD_LIB_2(settings.library, "gtk.cfg"); settings.addEnabled("all"); - +/* TEST_CASE(glib1); TEST_CASE(glib2); // #2806 - FP when using redundant assignment +*/ } void glib1() { @@ -6198,15 +5636,15 @@ private: // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this); checkMemoryLeak.checkReallocUsage(); - checkMemoryLeak.check(); } void run() override { LOAD_LIB_2(settings.library, "windows.cfg"); - +/* TEST_CASE(openfileNoLeak); TEST_CASE(returnValueNotUsed_tfopen_s); TEST_CASE(sendMessage); +*/ } void openfileNoLeak() {