"Fossies" - the Fresh Open Source Software Archive

Member "cppcheck-1.89/lib/checkleakautovar.cpp" (1 Sep 2019, 40750 Bytes) of package /windows/misc/cppcheck-1.89.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "checkleakautovar.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.88_vs_1.89.

    1 /*
    2  * Cppcheck - A tool for static C/C++ code analysis
    3  * Copyright (C) 2007-2019 Cppcheck team.
    4  *
    5  * This program is free software: you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation, either version 3 of the License, or
    8  * (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17  */
   18 
   19 //---------------------------------------------------------------------------
   20 // Leaks when using auto variables
   21 //---------------------------------------------------------------------------
   22 
   23 #include "checkleakautovar.h"
   24 
   25 #include "astutils.h"
   26 #include "checkmemoryleak.h"  // <- CheckMemoryLeak::memoryLeak
   27 #include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
   28 #include "errorlogger.h"
   29 #include "mathlib.h"
   30 #include "settings.h"
   31 #include "symboldatabase.h"
   32 #include "token.h"
   33 #include "tokenize.h"
   34 #include "valueflow.h"
   35 
   36 #include <cstddef>
   37 #include <iostream>
   38 #include <list>
   39 #include <stack>
   40 #include <utility>
   41 
   42 //---------------------------------------------------------------------------
   43 
   44 // Register this check class (by creating a static instance of it)
   45 namespace {
   46     CheckLeakAutoVar instance;
   47 }
   48 
   49 static const CWE CWE672(672U);
   50 static const CWE CWE415(415U);
   51 
   52 // Hardcoded allocation types (not from library)
   53 static const int NEW_ARRAY = -2;
   54 static const int NEW = -1;
   55 
   56 
   57 /**
   58  * @brief Is variable type some class with automatic deallocation?
   59  * @param vartok variable token
   60  * @return true unless it can be seen there is no automatic deallocation
   61  */
   62 static bool isAutoDealloc(const Variable *var)
   63 {
   64     if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE)
   65         return false;
   66 
   67     // return false if the type is a simple record type without side effects
   68     // a type that has no side effects (no constructors and no members with constructors)
   69     /** @todo false negative: check base class for side effects */
   70     /** @todo false negative: check constructors for side effects */
   71     if (var->typeScope() && var->typeScope()->numConstructors == 0 &&
   72         (var->typeScope()->varlist.empty() || var->type()->needInitialization == Type::NeedInitialization::True) &&
   73         var->type()->derivedFrom.empty())
   74         return false;
   75 
   76     return true;
   77 }
   78 
   79 //---------------------------------------------------------------------------
   80 
   81 void VarInfo::print()
   82 {
   83     std::cout << "size=" << alloctype.size() << std::endl;
   84     for (std::map<int, AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
   85         std::string strusage;
   86         const std::map<int, std::string>::const_iterator use =
   87             possibleUsage.find(it->first);
   88         if (use != possibleUsage.end())
   89             strusage = use->second;
   90 
   91         std::string status;
   92         switch (it->second.status) {
   93         case OWNED:
   94             status = "owned";
   95             break;
   96         case DEALLOC:
   97             status = "dealloc";
   98             break;
   99         case ALLOC:
  100             status = "alloc";
  101             break;
  102         case NOALLOC:
  103             status = "noalloc";
  104             break;
  105         default:
  106             status = "?";
  107             break;
  108         };
  109 
  110         std::cout << "status=" << status << " "
  111                   << "alloctype='" << it->second.type << "' "
  112                   << "possibleUsage='" << strusage << "' "
  113                   << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " "
  114                   << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " "
  115                   << std::endl;
  116     }
  117 }
  118 
  119 void VarInfo::possibleUsageAll(const std::string &functionName)
  120 {
  121     possibleUsage.clear();
  122     for (std::map<int, AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it)
  123         possibleUsage[it->first] = functionName;
  124 }
  125 
  126 
  127 void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type)
  128 {
  129     const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings);
  130     if (mSettings->library.isresource(type))
  131         checkmemleak.resourceLeakError(tok, varname);
  132     else
  133         checkmemleak.memleakError(tok, varname);
  134 }
  135 
  136 void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname)
  137 {
  138     const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings);
  139     const std::list<const Token *> callstack(1, tok);
  140     c.mismatchAllocDealloc(callstack, varname);
  141 }
  142 
  143 void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname)
  144 {
  145     const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings);
  146     c.deallocuseError(tok, varname);
  147 }
  148 
  149 void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname)
  150 {
  151     reportError(tok, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, false);
  152 }
  153 
  154 void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
  155 {
  156     if (mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) {
  157         reportError(tok,
  158                     Severity::information,
  159                     "checkLibraryUseIgnore",
  160                     "--check-library: Function " + functionName + "() should have <use>/<leak-ignore> configuration");
  161     }
  162 }
  163 
  164 void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
  165 {
  166     if (mSettings->library.isresource(type))
  167         reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, false);
  168     else
  169         reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, false);
  170 }
  171 
  172 
  173 void CheckLeakAutoVar::check()
  174 {
  175     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  176 
  177     // Local variables that are known to be non-zero.
  178     const std::set<int> notzero;
  179 
  180     // Check function scopes
  181     for (const Scope * scope : symbolDatabase->functionScopes) {
  182         if (scope->hasInlineOrLambdaFunction())
  183             continue;
  184 
  185         // Empty variable info
  186         VarInfo varInfo;
  187 
  188         checkScope(scope->bodyStart, &varInfo, notzero, 0);
  189 
  190         varInfo.conditionalAlloc.clear();
  191 
  192         // Clear reference arguments from varInfo..
  193         std::map<int, VarInfo::AllocInfo>::iterator it = varInfo.alloctype.begin();
  194         while (it != varInfo.alloctype.end()) {
  195             const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
  196             if (!var ||
  197                 (var->isArgument() && var->isReference()) ||
  198                 (!var->isArgument() && !var->isLocal()))
  199                 varInfo.alloctype.erase(it++);
  200             else
  201                 ++it;
  202         }
  203 
  204         ret(scope->bodyEnd, varInfo);
  205     }
  206 }
  207 
  208 static bool isVarUsedInTree(const Token *tok, nonneg int varid)
  209 {
  210     if (!tok)
  211         return false;
  212     if (tok->varId() == varid)
  213         return true;
  214     if (tok->str() == "(" && Token::simpleMatch(tok->astOperand1(), "sizeof"))
  215         return false;
  216     return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid);
  217 }
  218 
  219 static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid)
  220 {
  221     for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
  222         if (tok->varId() != varid)
  223             continue;
  224         if (Token::Match(tok, "%var% . release ( )"))
  225             return true;
  226         if (Token::Match(tok, "%var% ="))
  227             return false;
  228     }
  229     return false;
  230 }
  231 
  232 static bool isLocalVarNoAutoDealloc(const Token *varTok, const bool isCpp)
  233 {
  234     // not a local variable nor argument?
  235     const Variable *var = varTok->variable();
  236     if (!var)
  237         return true;
  238     if (!var->isArgument() && (!var->isLocal() || var->isStatic()))
  239         return false;
  240 
  241     // Don't check reference variables
  242     if (var->isReference())
  243         return false;
  244 
  245     // non-pod variable
  246     if (isCpp) {
  247         // Possibly automatically deallocated memory
  248         if (isAutoDealloc(var) && Token::Match(varTok, "%var% = new"))
  249             return false;
  250         if (!var->isPointer() && !var->typeStartToken()->isStandardType())
  251             return false;
  252     }
  253     return true;
  254 }
  255 
  256 /** checks if nameToken is a name of a function in a function call:
  257 *     func(arg)
  258 * or
  259 *     func<temp1_arg>(arg)
  260 * @param nameToken Function name token
  261 * @return opening parenthesis token or NULL if not a function call
  262 */
  263 
  264 static const Token * isFunctionCall(const Token * nameToken)
  265 {
  266     if (nameToken->isName()) {
  267         nameToken = nameToken->next();
  268         // check if function is a template
  269         if (nameToken && nameToken->link() && nameToken->str() == "<") {
  270             // skip template arguments
  271             nameToken = nameToken->link()->next();
  272         }
  273         // check for '('
  274         if (nameToken && nameToken->link() && nameToken->str() == "(") {
  275             // returning opening parenthesis pointer
  276             return nameToken;
  277         }
  278     }
  279     return nullptr;
  280 }
  281 
  282 void CheckLeakAutoVar::checkScope(const Token * const startToken,
  283                                   VarInfo *varInfo,
  284                                   std::set<int> notzero,
  285                                   nonneg int recursiveCount)
  286 {
  287 #if ASAN
  288     static const nonneg int recursiveLimit = 300;
  289 #else
  290     static const nonneg int recursiveLimit = 1000;
  291 #endif
  292     if (++recursiveCount > recursiveLimit)    // maximum number of "else if ()"
  293         throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT);
  294 
  295     std::map<int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
  296     std::map<int, std::string> &possibleUsage = varInfo->possibleUsage;
  297     const std::set<int> conditionalAlloc(varInfo->conditionalAlloc);
  298 
  299     // Parse all tokens
  300     const Token * const endToken = startToken->link();
  301     for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
  302         if (!tok->scope()->isExecutable()) {
  303             tok = tok->scope()->bodyEnd;
  304             if (!tok) // Ticket #6666 (crash upon invalid code)
  305                 break;
  306         }
  307 
  308         // check each token
  309         {
  310             const Token * nextTok = checkTokenInsideExpression(tok, varInfo);
  311             if (nextTok) {
  312                 tok = nextTok;
  313                 continue;
  314             }
  315         }
  316 
  317 
  318         // look for end of statement
  319         if (!Token::Match(tok, "[;{},]") || Token::Match(tok->next(), "[;{},]"))
  320             continue;
  321 
  322         tok = tok->next();
  323         if (!tok || tok == endToken)
  324             break;
  325 
  326         // parse statement, skip to last member
  327         const Token *varTok = tok;
  328         while (Token::Match(varTok, "%name% ::|. %name% !!("))
  329             varTok = varTok->tokAt(2);
  330 
  331         const Token *ftok = tok;
  332         if (ftok->str() == "::")
  333             ftok = ftok->next();
  334         while (Token::Match(ftok, "%name% :: %name%"))
  335             ftok = ftok->tokAt(2);
  336 
  337         // assignment..
  338         if (Token::Match(varTok, "%var% =")) {
  339             const Token* const tokAssignOp = varTok->next();
  340 
  341             // taking address of another variable..
  342             if (Token::Match(tokAssignOp, "= %var% [+;]")) {
  343                 if (varTok->tokAt(2)->varId() != varTok->varId()) {
  344                     // If variable points at allocated memory => error
  345                     leakIfAllocated(varTok, *varInfo);
  346 
  347                     // no multivariable checking currently => bail out for rhs variables
  348                     for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) {
  349                         if (tok2->str() == ";") {
  350                             break;
  351                         }
  352                         if (tok2->varId()) {
  353                             varInfo->erase(tok2->varId());
  354                         }
  355                     }
  356                 }
  357             }
  358 
  359             // right ast part (after `=` operator)
  360             const Token* tokRightAstOperand = tokAssignOp->astOperand2();
  361             while (tokRightAstOperand && tokRightAstOperand->isCast())
  362                 tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
  363 
  364             // is variable used in rhs?
  365             if (isVarUsedInTree(tokRightAstOperand, varTok->varId()))
  366                 continue;
  367 
  368             // Variable has already been allocated => error
  369             if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end())
  370                 leakIfAllocated(varTok, *varInfo);
  371             varInfo->erase(varTok->varId());
  372 
  373             if (!isLocalVarNoAutoDealloc(varTok, mTokenizer->isCPP()))
  374                 continue;
  375 
  376             // allocation?
  377             const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr;
  378             if (Token::Match(fTok, "%type% (")) {
  379                 const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok);
  380                 if (f && f->arg == -1) {
  381                     VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
  382                     varAlloc.type = f->groupId;
  383                     varAlloc.status = VarInfo::ALLOC;
  384                 }
  385 
  386                 changeAllocStatusIfRealloc(alloctype, fTok, varTok);
  387             } else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) {
  388                 const Token* tok2 = varTok->tokAt(2)->astOperand1();
  389                 const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
  390                 VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
  391                 varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
  392                 varAlloc.status = VarInfo::ALLOC;
  393             }
  394 
  395             // Assigning non-zero value variable. It might be used to
  396             // track the execution for a later if condition.
  397             if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toLongNumber(varTok->strAt(2)) != 0)
  398                 notzero.insert(varTok->varId());
  399             else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName())
  400                 notzero.insert(varTok->varId());
  401             else
  402                 notzero.erase(varTok->varId());
  403         }
  404 
  405         // if/else
  406         else if (Token::simpleMatch(tok, "if (")) {
  407             // Parse function calls inside the condition
  408 
  409             const Token * closingParenthesis = tok->linkAt(1);
  410             for (const Token *innerTok = tok->tokAt(2); innerTok && innerTok != closingParenthesis; innerTok = innerTok->next()) {
  411                 // TODO: replace with checkTokenInsideExpression()
  412 
  413                 if (!isLocalVarNoAutoDealloc(innerTok, mTokenizer->isCPP()))
  414                     continue;
  415 
  416                 if (Token::Match(innerTok, "%var% =") && innerTok->astParent() == innerTok->next()) {
  417                     // allocation?
  418                     // right ast part (after `=` operator)
  419                     const Token* tokRightAstOperand = innerTok->next()->astOperand2();
  420                     while (tokRightAstOperand && tokRightAstOperand->isCast())
  421                         tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
  422                     if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
  423                         const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(tokRightAstOperand->previous());
  424                         if (f && f->arg == -1) {
  425                             VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
  426                             varAlloc.type = f->groupId;
  427                             varAlloc.status = VarInfo::ALLOC;
  428                         } else {
  429                             // Fixme: warn about leak
  430                             alloctype.erase(innerTok->varId());
  431                         }
  432 
  433                         changeAllocStatusIfRealloc(alloctype, innerTok->tokAt(2), varTok);
  434                     } else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) {
  435                         const Token* tok2 = innerTok->tokAt(2)->astOperand1();
  436                         const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
  437                         VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
  438                         varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
  439                         varAlloc.status = VarInfo::ALLOC;
  440                     }
  441                 }
  442 
  443                 // check for function call
  444                 const Token * const openingPar = isFunctionCall(innerTok);
  445                 if (openingPar) {
  446                     // innerTok is a function name
  447                     const VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
  448                     functionCall(innerTok, openingPar, varInfo, allocation, nullptr);
  449                     innerTok = openingPar->link();
  450                 }
  451             }
  452 
  453             if (Token::simpleMatch(closingParenthesis, ") {")) {
  454                 VarInfo varInfo1(*varInfo);  // VarInfo for if code
  455                 VarInfo varInfo2(*varInfo);  // VarInfo for else code
  456 
  457                 // Recursively scan variable comparisons in condition
  458                 std::stack<const Token *> tokens;
  459                 tokens.push(tok->next()->astOperand2());
  460                 while (!tokens.empty()) {
  461                     const Token *tok3 = tokens.top();
  462                     tokens.pop();
  463                     if (!tok3)
  464                         continue;
  465                     if (tok3->str() == "&&" || tok3->str() == "||") {
  466                         // FIXME: handle && ! || better
  467                         tokens.push(tok3->astOperand1());
  468                         tokens.push(tok3->astOperand2());
  469                         continue;
  470                     }
  471                     if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
  472                         tokens.push(tok3->astOperand2());
  473                         continue;
  474                     } else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) {
  475                         const std::vector<const Token *> params = getArguments(tok3->previous());
  476                         for (const Token *par : params) {
  477                             if (!par->isComparisonOp())
  478                                 continue;
  479                             const Token *vartok = nullptr;
  480                             if (astIsVariableComparison(par, "!=", "0", &vartok) ||
  481                                 astIsVariableComparison(par, "==", "0", &vartok) ||
  482                                 astIsVariableComparison(par, "<", "0", &vartok) ||
  483                                 astIsVariableComparison(par, ">", "0", &vartok) ||
  484                                 astIsVariableComparison(par, "==", "-1", &vartok) ||
  485                                 astIsVariableComparison(par, "!=", "-1", &vartok)) {
  486                                 varInfo1.erase(vartok->varId());
  487                                 varInfo2.erase(vartok->varId());
  488                             }
  489                         }
  490                         continue;
  491                     }
  492 
  493                     const Token *vartok = nullptr;
  494                     if (astIsVariableComparison(tok3, "!=", "0", &vartok)) {
  495                         varInfo2.erase(vartok->varId());
  496                         if (notzero.find(vartok->varId()) != notzero.end())
  497                             varInfo2.clear();
  498                     } else if (astIsVariableComparison(tok3, "==", "0", &vartok)) {
  499                         varInfo1.erase(vartok->varId());
  500                     } else if (astIsVariableComparison(tok3, "<", "0", &vartok)) {
  501                         varInfo1.erase(vartok->varId());
  502                     } else if (astIsVariableComparison(tok3, ">", "0", &vartok)) {
  503                         varInfo2.erase(vartok->varId());
  504                     } else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) {
  505                         varInfo1.erase(vartok->varId());
  506                     }
  507                 }
  508 
  509                 checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount);
  510                 closingParenthesis = closingParenthesis->linkAt(1);
  511                 if (Token::simpleMatch(closingParenthesis, "} else {")) {
  512                     checkScope(closingParenthesis->tokAt(2), &varInfo2, notzero, recursiveCount);
  513                     tok = closingParenthesis->linkAt(2)->previous();
  514                 } else {
  515                     tok = closingParenthesis->previous();
  516                 }
  517 
  518                 VarInfo old;
  519                 old.swap(*varInfo);
  520 
  521                 std::map<int, VarInfo::AllocInfo>::const_iterator it;
  522 
  523                 for (it = old.alloctype.begin(); it != old.alloctype.end(); ++it) {
  524                     const int varId = it->first;
  525                     if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end())
  526                         continue;
  527                     if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() ||
  528                         varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) {
  529                         varInfo1.erase(varId);
  530                         varInfo2.erase(varId);
  531                     }
  532                 }
  533 
  534                 // Conditional allocation in varInfo1
  535                 for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
  536                     if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
  537                         old.alloctype.find(it->first) == old.alloctype.end()) {
  538                         varInfo->conditionalAlloc.insert(it->first);
  539                     }
  540                 }
  541 
  542                 // Conditional allocation in varInfo2
  543                 for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
  544                     if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() &&
  545                         old.alloctype.find(it->first) == old.alloctype.end()) {
  546                         varInfo->conditionalAlloc.insert(it->first);
  547                     }
  548                 }
  549 
  550                 // Conditional allocation/deallocation
  551                 for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
  552                     if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
  553                         varInfo->conditionalAlloc.erase(it->first);
  554                         varInfo2.erase(it->first);
  555                     }
  556                 }
  557                 for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
  558                     if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
  559                         varInfo->conditionalAlloc.erase(it->first);
  560                         varInfo1.erase(it->first);
  561                     }
  562                 }
  563 
  564                 alloctype.insert(varInfo1.alloctype.begin(), varInfo1.alloctype.end());
  565                 alloctype.insert(varInfo2.alloctype.begin(), varInfo2.alloctype.end());
  566 
  567                 possibleUsage.insert(varInfo1.possibleUsage.begin(), varInfo1.possibleUsage.end());
  568                 possibleUsage.insert(varInfo2.possibleUsage.begin(), varInfo2.possibleUsage.end());
  569             }
  570         }
  571 
  572         // unknown control.. (TODO: handle loops)
  573         else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) {
  574             varInfo->clear();
  575             break;
  576         }
  577 
  578         // return
  579         else if (tok->str() == "return") {
  580             ret(tok, *varInfo);
  581             varInfo->clear();
  582         }
  583 
  584         // throw
  585         else if (mTokenizer->isCPP() && tok->str() == "throw") {
  586             bool tryFound = false;
  587             const Scope* scope = tok->scope();
  588             while (scope && scope->isExecutable()) {
  589                 if (scope->type == Scope::eTry)
  590                     tryFound = true;
  591                 scope = scope->nestedIn;
  592             }
  593             // If the execution leaves the function then treat it as return
  594             if (!tryFound)
  595                 ret(tok, *varInfo);
  596             varInfo->clear();
  597         }
  598 
  599         // delete
  600         else if (mTokenizer->isCPP() && tok->str() == "delete") {
  601             const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]");
  602             if (arrayDelete)
  603                 tok = tok->tokAt(3);
  604             else
  605                 tok = tok->next();
  606             if (tok->str() == "(")
  607                 tok = tok->next();
  608             while (Token::Match(tok, "%name% ::|."))
  609                 tok = tok->tokAt(2);
  610             const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0;
  611             if (!isnull && tok->varId() && tok->strAt(1) != "[") {
  612                 const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC);
  613                 changeAllocStatus(varInfo, allocation, tok, tok);
  614             }
  615         }
  616 
  617         // Function call..
  618         else if (isFunctionCall(ftok)) {
  619             const Token * openingPar = isFunctionCall(ftok);
  620             const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok);
  621             VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
  622             if (allocation.type == 0)
  623                 allocation.status = VarInfo::NOALLOC;
  624             functionCall(ftok, openingPar, varInfo, allocation, af);
  625 
  626             tok = ftok->next()->link();
  627 
  628             // Handle scopes that might be noreturn
  629             if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) {
  630                 const std::string &functionName(tok->link()->previous()->str());
  631                 bool unknown = false;
  632                 if (mTokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
  633                     if (!unknown)
  634                         varInfo->clear();
  635                     else if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName))
  636                         varInfo->possibleUsageAll(functionName);
  637                 }
  638             }
  639 
  640             continue;
  641         }
  642 
  643         // goto => weird execution path
  644         else if (tok->str() == "goto") {
  645             varInfo->clear();
  646         }
  647 
  648         // continue/break
  649         else if (Token::Match(tok, "continue|break ;")) {
  650             varInfo->clear();
  651         }
  652 
  653         // Check smart pointer
  654         else if (Token::Match(ftok, "%name% <") && mSettings->library.isSmartPointer(tok)) {
  655             const Token * typeEndTok = ftok->linkAt(1);
  656             if (!Token::Match(typeEndTok, "> %var% {|( %var% ,|)|}"))
  657                 continue;
  658 
  659             tok = typeEndTok->linkAt(2);
  660 
  661             const int varid = typeEndTok->next()->varId();
  662             if (isPointerReleased(typeEndTok->tokAt(2), endToken, varid))
  663                 continue;
  664 
  665             bool arrayDelete = false;
  666             if (Token::findsimplematch(ftok->next(), "[ ]", typeEndTok))
  667                 arrayDelete = true;
  668 
  669             // Check deleter
  670             const Token * deleterToken = nullptr;
  671             const Token * endDeleterToken = nullptr;
  672             const Library::AllocFunc* af = nullptr;
  673             if (Token::Match(ftok, "unique_ptr < %type% ,")) {
  674                 deleterToken = ftok->tokAt(4);
  675                 endDeleterToken = typeEndTok;
  676             } else if (Token::Match(typeEndTok, "> %var% {|( %var% ,")) {
  677                 deleterToken = typeEndTok->tokAt(5);
  678                 endDeleterToken = typeEndTok->linkAt(2);
  679             }
  680             if (deleterToken) {
  681                 // Skip the decaying plus in expressions like +[](T*){}
  682                 if (deleterToken->str() == "+") {
  683                     deleterToken = deleterToken->next();
  684                 }
  685                 // Check if its a pointer to a function
  686                 const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
  687                 if (dtok) {
  688                     af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
  689                 } else {
  690                     const Token * tscopeStart = nullptr;
  691                     const Token * tscopeEnd = nullptr;
  692                     // If the deleter is a lambda, check if it calls the dealloc function
  693                     if (deleterToken->str() == "[" &&
  694                         Token::simpleMatch(deleterToken->link(), "] (") &&
  695                         // TODO: Check for mutable keyword
  696                         Token::simpleMatch(deleterToken->link()->linkAt(1), ") {")) {
  697                         tscopeStart = deleterToken->link()->linkAt(1)->tokAt(1);
  698                         tscopeEnd = tscopeStart->link();
  699                         // If the deleter is a class, check if class calls the dealloc function
  700                     } else if ((dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken)) && dtok->type()) {
  701                         const Scope * tscope = dtok->type()->classScope;
  702                         if (tscope) {
  703                             tscopeStart = tscope->bodyStart;
  704                             tscopeEnd = tscope->bodyEnd;
  705                         }
  706                     }
  707 
  708                     if (tscopeStart && tscopeEnd) {
  709                         for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) {
  710                             af = mSettings->library.getDeallocFuncInfo(tok2);
  711                             if (af)
  712                                 break;
  713                         }
  714                     }
  715                 }
  716             }
  717 
  718             const Token * vtok = typeEndTok->tokAt(3);
  719             const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED);
  720             changeAllocStatus(varInfo, allocation, vtok, vtok);
  721         }
  722     }
  723 }
  724 
  725 
  726 const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo *varInfo)
  727 {
  728     // Deallocation and then dereferencing pointer..
  729     if (tok->varId() > 0) {
  730         // TODO : Write a separate checker for this that uses valueFlowForward.
  731         const std::map<int, VarInfo::AllocInfo>::const_iterator var = varInfo->alloctype.find(tok->varId());
  732         if (var != varInfo->alloctype.end()) {
  733             bool unknown = false;
  734             if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok, unknown, mSettings) && !unknown) {
  735                 deallocUseError(tok, tok->str());
  736             } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
  737                 varInfo->erase(tok->varId());
  738             } else if (Token::Match(tok->previous(), "= %var% [;,)]")) {
  739                 varInfo->erase(tok->varId());
  740             }
  741         } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) {
  742             varInfo->referenced.insert(tok->tokAt(2)->varId());
  743         }
  744     }
  745 
  746     // check for function call
  747     const Token * const openingPar = isFunctionCall(tok);
  748     if (openingPar) {
  749         const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok);
  750         VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC);
  751         if (alloc.type == 0)
  752             alloc.status = VarInfo::NOALLOC;
  753         functionCall(tok, openingPar, varInfo, alloc, nullptr);
  754         return openingPar->link();
  755     }
  756 
  757     return nullptr;
  758 }
  759 
  760 
  761 void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map<int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok)
  762 {
  763     const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok);
  764     if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) {
  765         const Token* argTok = getArguments(fTok).at(f->reallocArg - 1);
  766         VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()];
  767         VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()];
  768         if (argAlloc.type != 0 && argAlloc.type != f->groupId)
  769             mismatchError(fTok, argTok->str());
  770         argAlloc.status = VarInfo::DEALLOC;
  771         retAlloc.type = f->groupId;
  772         retAlloc.status = VarInfo::ALLOC;
  773     }
  774 }
  775 
  776 
  777 void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
  778 {
  779     std::map<int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
  780     const std::map<int, VarInfo::AllocInfo>::iterator var = alloctype.find(arg->varId());
  781     if (var != alloctype.end()) {
  782         if (allocation.status == VarInfo::NOALLOC) {
  783             // possible usage
  784             varInfo->possibleUsage[arg->varId()] = tok->str();
  785             if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
  786                 varInfo->erase(arg->varId());
  787         } else if (var->second.managed()) {
  788             doubleFreeError(tok, arg->str(), allocation.type);
  789         } else if (var->second.type != allocation.type) {
  790             // mismatching allocation and deallocation
  791             mismatchError(tok, arg->str());
  792             varInfo->erase(arg->varId());
  793         } else {
  794             // deallocation
  795             var->second.status = allocation.status;
  796             var->second.type = allocation.type;
  797         }
  798     } else if (allocation.status != VarInfo::NOALLOC) {
  799         alloctype[arg->varId()].status = VarInfo::DEALLOC;
  800     }
  801 }
  802 
  803 void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af)
  804 {
  805     // Ignore function call?
  806     if (mSettings->library.isLeakIgnore(tokName->str()))
  807         return;
  808     if (mSettings->library.getReallocFuncInfo(tokName))
  809         return;
  810 
  811     const Token * const tokFirstArg = tokOpeningPar->next();
  812     if (!tokFirstArg || tokFirstArg->str() == ")") {
  813         // no arguments
  814         return;
  815     }
  816 
  817     int argNr = 1;
  818     for (const Token *arg = tokFirstArg; arg; arg = arg->nextArgument()) {
  819         if (mTokenizer->isCPP() && arg->str() == "new") {
  820             arg = arg->next();
  821             if (Token::simpleMatch(arg, "( std :: nothrow )"))
  822                 arg = arg->tokAt(5);
  823         }
  824 
  825         // Skip casts
  826         while (arg && arg->isCast())
  827             arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1();
  828         const Token * const argTypeStartTok = arg;
  829 
  830         while (Token::Match(arg, "%name% .|:: %name%"))
  831             arg = arg->tokAt(2);
  832 
  833         if (Token::Match(arg, "%var% [-,)] !!.") || Token::Match(arg, "& %var%")) {
  834             // goto variable
  835             if (arg->str() == "&")
  836                 arg = arg->next();
  837 
  838             const bool isnull = arg->hasKnownIntValue() && arg->values().front().intvalue == 0;
  839 
  840             // Is variable allocated?
  841             if (!isnull && (!af || af->arg == argNr))
  842                 changeAllocStatus(varInfo, allocation, tokName, arg);
  843         }
  844         // Check smart pointer
  845         else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) {
  846             const Token * typeEndTok = arg->linkAt(1);
  847             if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}"))
  848                 continue;
  849 
  850             bool arrayDelete = false;
  851             if (Token::findsimplematch(arg->next(), "[ ]", typeEndTok))
  852                 arrayDelete = true;
  853 
  854             // Check deleter
  855             const Token * deleterToken = nullptr;
  856             const Token * endDeleterToken = nullptr;
  857             const Library::AllocFunc* sp_af = nullptr;
  858             if (Token::Match(arg, "unique_ptr < %type% ,")) {
  859                 deleterToken = arg->tokAt(4);
  860                 endDeleterToken = typeEndTok;
  861             } else if (Token::Match(typeEndTok, "> {|( %var% ,")) {
  862                 deleterToken = typeEndTok->tokAt(4);
  863                 endDeleterToken = typeEndTok->linkAt(1);
  864             }
  865             if (deleterToken) {
  866                 // Check if its a pointer to a function
  867                 const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
  868                 if (dtok) {
  869                     sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
  870                 } else {
  871                     // If the deleter is a class, check if class calls the dealloc function
  872                     dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken);
  873                     if (dtok && dtok->type()) {
  874                         const Scope * tscope = dtok->type()->classScope;
  875                         for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) {
  876                             sp_af = mSettings->library.getDeallocFuncInfo(tok2);
  877                             if (sp_af)
  878                                 break;
  879                         }
  880                     }
  881                 }
  882             }
  883 
  884             const Token * vtok = typeEndTok->tokAt(2);
  885             const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED);
  886             changeAllocStatus(varInfo, sp_allocation, vtok, vtok);
  887         } else {
  888             checkTokenInsideExpression(arg, varInfo);
  889         }
  890         // TODO: check each token in argument expression (could contain multiple variables)
  891         argNr++;
  892     }
  893 }
  894 
  895 
  896 void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
  897                                        const VarInfo &varInfo)
  898 {
  899     const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
  900     const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
  901 
  902     const std::map<int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
  903     if (var != alloctype.end() && var->second.status == VarInfo::ALLOC) {
  904         const std::map<int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
  905         if (use == possibleUsage.end()) {
  906             leakError(vartok, vartok->str(), var->second.type);
  907         } else {
  908             configurationInfo(vartok, use->second);
  909         }
  910     }
  911 }
  912 
  913 void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
  914 {
  915     const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
  916     const std::map<int, std::string> &possibleUsage = varInfo.possibleUsage;
  917 
  918     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
  919     for (std::map<int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
  920         // don't warn if variable is conditionally allocated
  921         if (!it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
  922             continue;
  923 
  924         // don't warn if there is a reference of the variable
  925         if (varInfo.referenced.find(it->first) != varInfo.referenced.end())
  926             continue;
  927 
  928         const int varid = it->first;
  929         const Variable *var = symbolDatabase->getVariableFromVarId(varid);
  930         if (var) {
  931             bool used = false;
  932             for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
  933                 if (tok2->str() == ";")
  934                     break;
  935                 if (Token::Match(tok2, "return|(|{|, %varid% [});,]", varid)) {
  936                     used = true;
  937                     break;
  938                 }
  939                 if (Token::Match(tok2, "return|(|{|, & %varid% . %name% [});,]", varid)) {
  940                     used = true;
  941                     break;
  942                 }
  943             }
  944 
  945             // return deallocated pointer
  946             if (used && it->second.status == VarInfo::DEALLOC)
  947                 deallocReturnError(tok, var->name());
  948 
  949             else if (!used && !it->second.managed()) {
  950                 const std::map<int, std::string>::const_iterator use = possibleUsage.find(varid);
  951                 if (use == possibleUsage.end()) {
  952                     leakError(tok, var->name(), it->second.type);
  953                 } else {
  954                     configurationInfo(tok, use->second);
  955                 }
  956             }
  957         }
  958     }
  959 }