Blame lib/checkleakautovar.cpp

Packit 2035a7
/*
Packit 2035a7
 * Cppcheck - A tool for static C/C++ code analysis
Packit 2035a7
 * Copyright (C) 2007-2017 Cppcheck team.
Packit 2035a7
 *
Packit 2035a7
 * This program is free software: you can redistribute it and/or modify
Packit 2035a7
 * it under the terms of the GNU General Public License as published by
Packit 2035a7
 * the Free Software Foundation, either version 3 of the License, or
Packit 2035a7
 * (at your option) any later version.
Packit 2035a7
 *
Packit 2035a7
 * This program is distributed in the hope that it will be useful,
Packit 2035a7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2035a7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 2035a7
 * GNU General Public License for more details.
Packit 2035a7
 *
Packit 2035a7
 * You should have received a copy of the GNU General Public License
Packit 2035a7
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 2035a7
 */
Packit 2035a7
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
// Leaks when using auto variables
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
#include "checkleakautovar.h"
Packit 2035a7
Packit 2035a7
#include "astutils.h"
Packit 2035a7
#include "checkmemoryleak.h"  // <- CheckMemoryLeak::memoryLeak
Packit 2035a7
#include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
Packit 2035a7
#include "errorlogger.h"
Packit 2035a7
#include "mathlib.h"
Packit 2035a7
#include "settings.h"
Packit 2035a7
#include "symboldatabase.h"
Packit 2035a7
#include "token.h"
Packit 2035a7
#include "tokenize.h"
Packit 2035a7
#include "valueflow.h"
Packit 2035a7
Packit 2035a7
#include <cstddef>
Packit 2035a7
#include <iostream>
Packit 2035a7
#include <list>
Packit 2035a7
#include <stack>
Packit 2035a7
#include <utility>
Packit 2035a7
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
// Register this check class (by creating a static instance of it)
Packit 2035a7
namespace {
Packit 2035a7
    CheckLeakAutoVar instance;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static const CWE CWE672(672U);
Packit 2035a7
static const CWE CWE415(415U);
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
void VarInfo::print()
Packit 2035a7
{
Packit 2035a7
    std::cout << "size=" << alloctype.size() << std::endl;
Packit 2035a7
    std::map<unsigned int, AllocInfo>::const_iterator it;
Packit 2035a7
    for (it = alloctype.begin(); it != alloctype.end(); ++it) {
Packit 2035a7
        std::string strusage;
Packit 2035a7
        std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(it->first);
Packit 2035a7
        if (use != possibleUsage.end())
Packit 2035a7
            strusage = use->second;
Packit 2035a7
Packit 2035a7
        std::string status;
Packit 2035a7
        switch (it->second.status) {
Packit 2035a7
        case DEALLOC:
Packit 2035a7
            status = "dealloc";
Packit 2035a7
            break;
Packit 2035a7
        case ALLOC:
Packit 2035a7
            status = "alloc";
Packit 2035a7
            break;
Packit 2035a7
        case NOALLOC:
Packit 2035a7
            status = "noalloc";
Packit 2035a7
            break;
Packit 2035a7
        default:
Packit 2035a7
            status = "?";
Packit 2035a7
            break;
Packit 2035a7
        };
Packit 2035a7
Packit 2035a7
        std::cout << "status=" << status << " "
Packit 2035a7
                  << "alloctype='" << it->second.type << "' "
Packit 2035a7
                  << "possibleUsage='" << strusage << "' "
Packit 2035a7
                  << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " "
Packit 2035a7
                  << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " "
Packit 2035a7
                  << std::endl;
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void VarInfo::possibleUsageAll(const std::string &functionName)
Packit 2035a7
{
Packit 2035a7
    possibleUsage.clear();
Packit 2035a7
    std::map<unsigned int, AllocInfo>::const_iterator it;
Packit 2035a7
    for (it = alloctype.begin(); it != alloctype.end(); ++it)
Packit 2035a7
        possibleUsage[it->first] = functionName;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type)
Packit 2035a7
{
Packit 2035a7
    const CheckMemoryLeak checkmemleak(_tokenizer, _errorLogger, _settings);
Packit 2035a7
    if (_settings->library.isresource(type))
Packit 2035a7
        checkmemleak.resourceLeakError(tok, varname);
Packit 2035a7
    else
Packit 2035a7
        checkmemleak.memleakError(tok, varname);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname)
Packit 2035a7
{
Packit 2035a7
    const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings);
Packit 2035a7
    std::list<const Token *> callstack(1, tok);
Packit 2035a7
    c.mismatchAllocDealloc(callstack, varname);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname)
Packit 2035a7
{
Packit 2035a7
    const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings);
Packit 2035a7
    c.deallocuseError(tok, varname);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::error, "deallocret", "Returning/dereferencing '" + varname + "' after it is deallocated / released", CWE672, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
Packit 2035a7
{
Packit 2035a7
    if (_settings->checkLibrary && _settings->isEnabled(Settings::INFORMATION)) {
Packit 2035a7
        reportError(tok,
Packit 2035a7
                    Severity::information,
Packit 2035a7
                    "checkLibraryUseIgnore",
Packit 2035a7
                    "--check-library: Function " + functionName + "() should have <use>/<leak-ignore> configuration");
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
Packit 2035a7
{
Packit 2035a7
    if (_settings->library.isresource(type))
Packit 2035a7
        reportError(tok, Severity::error, "doubleFree", "Resource handle '" + varname + "' freed twice.", CWE415, false);
Packit 2035a7
    else
Packit 2035a7
        reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname + "' is freed twice.", CWE415, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::check()
Packit 2035a7
{
Packit 2035a7
    const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
Packit 2035a7
Packit 2035a7
    // Local variables that are known to be non-zero.
Packit 2035a7
    const std::set<unsigned int> notzero;
Packit 2035a7
Packit 2035a7
    // Check function scopes
Packit 2035a7
    const std::size_t functions = symbolDatabase->functionScopes.size();
Packit 2035a7
    for (std::size_t i = 0; i < functions; ++i) {
Packit 2035a7
        const Scope * scope = symbolDatabase->functionScopes[i];
Packit 2035a7
        if (scope->hasInlineOrLambdaFunction())
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        // Empty variable info
Packit 2035a7
        VarInfo varInfo;
Packit 2035a7
Packit 2035a7
        checkScope(scope->classStart, &varInfo, notzero);
Packit 2035a7
Packit 2035a7
        varInfo.conditionalAlloc.clear();
Packit 2035a7
Packit 2035a7
        // Clear reference arguments from varInfo..
Packit 2035a7
        std::map<unsigned int, VarInfo::AllocInfo>::iterator it = varInfo.alloctype.begin();
Packit 2035a7
        while (it != varInfo.alloctype.end()) {
Packit 2035a7
            const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
Packit 2035a7
            if (!var ||
Packit 2035a7
                (var->isArgument() && var->isReference()) ||
Packit 2035a7
                (!var->isArgument() && !var->isLocal()))
Packit 2035a7
                varInfo.alloctype.erase(it++);
Packit 2035a7
            else
Packit 2035a7
                ++it;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        ret(scope->classEnd, varInfo);
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
static bool isVarUsedInTree(const Token *tok, unsigned int varid)
Packit 2035a7
{
Packit 2035a7
    if (!tok)
Packit 2035a7
        return false;
Packit 2035a7
    if (tok->varId() == varid)
Packit 2035a7
        return true;
Packit 2035a7
    return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::checkScope(const Token * const startToken,
Packit 2035a7
                                  VarInfo *varInfo,
Packit 2035a7
                                  std::set<unsigned int> notzero)
Packit 2035a7
{
Packit 2035a7
    std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
Packit 2035a7
    std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
Packit 2035a7
    const std::set<unsigned int> conditionalAlloc(varInfo->conditionalAlloc);
Packit 2035a7
Packit 2035a7
    // Parse all tokens
Packit 2035a7
    const Token * const endToken = startToken->link();
Packit 2035a7
    for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
Packit 2035a7
        if (!tok->scope()->isExecutable()) {
Packit 2035a7
            tok = tok->scope()->classEnd;
Packit 2035a7
            if (!tok) // Ticket #6666 (crash upon invalid code)
Packit 2035a7
                break;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // Deallocation and then dereferencing pointer..
Packit 2035a7
        if (tok->varId() > 0) {
Packit 2035a7
            const std::map<unsigned int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(tok->varId());
Packit 2035a7
            if (var != alloctype.end()) {
Packit 2035a7
                bool unknown = false;
Packit 2035a7
                if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok,unknown) && !unknown) {
Packit 2035a7
                    deallocUseError(tok, tok->str());
Packit 2035a7
                } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
Packit 2035a7
                    varInfo->erase(tok->varId());
Packit 2035a7
                } else if (tok->strAt(-1) == "=") {
Packit 2035a7
                    varInfo->erase(tok->varId());
Packit 2035a7
                }
Packit 2035a7
            } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) {
Packit 2035a7
                varInfo->referenced.insert(tok->tokAt(2)->varId());
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        if (tok->str() == "(" && tok->previous()->isName()) {
Packit 2035a7
            VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
Packit 2035a7
            functionCall(tok->previous(), varInfo, allocation, nullptr);
Packit 2035a7
            tok = tok->link();
Packit 2035a7
            continue;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // look for end of statement
Packit 2035a7
        if (!Token::Match(tok, "[;{}]") || Token::Match(tok->next(), "[;{}]"))
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        tok = tok->next();
Packit 2035a7
        if (!tok || tok == endToken)
Packit 2035a7
            break;
Packit 2035a7
Packit 2035a7
        // parse statement, skip to last member
Packit 2035a7
        const Token *varTok = tok;
Packit 2035a7
        while (Token::Match(varTok, "%name% ::|. %name% !!("))
Packit 2035a7
            varTok = varTok->tokAt(2);
Packit 2035a7
Packit 2035a7
        const Token *ftok = tok;
Packit 2035a7
        if (ftok->str() == "::")
Packit 2035a7
            ftok = ftok->next();
Packit 2035a7
        while (Token::Match(ftok, "%name% :: %name%"))
Packit 2035a7
            ftok = ftok->tokAt(2);
Packit 2035a7
Packit 2035a7
        // assignment..
Packit 2035a7
        if (Token::Match(varTok, "%var% =")) {
Packit 2035a7
            // taking address of another variable..
Packit 2035a7
            if (Token::Match(varTok->next(), "= %var% [+;]")) {
Packit 2035a7
                if (varTok->tokAt(2)->varId() != varTok->varId()) {
Packit 2035a7
                    // If variable points at allocated memory => error
Packit 2035a7
                    leakIfAllocated(varTok, *varInfo);
Packit 2035a7
Packit 2035a7
                    // no multivariable checking currently => bail out for rhs variables
Packit 2035a7
                    for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) {
Packit 2035a7
                        if (tok2->str() == ";") {
Packit 2035a7
                            break;
Packit 2035a7
                        }
Packit 2035a7
                        if (tok2->varId()) {
Packit 2035a7
                            varInfo->erase(tok2->varId());
Packit 2035a7
                        }
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            // is variable used in rhs?
Packit 2035a7
            if (isVarUsedInTree(varTok->next()->astOperand2(), varTok->varId()))
Packit 2035a7
                continue;
Packit 2035a7
Packit 2035a7
            // Variable has already been allocated => error
Packit 2035a7
            if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end())
Packit 2035a7
                leakIfAllocated(varTok, *varInfo);
Packit 2035a7
            varInfo->erase(varTok->varId());
Packit 2035a7
Packit 2035a7
            // not a local variable nor argument?
Packit 2035a7
            const Variable *var = varTok->variable();
Packit 2035a7
            if (var && !var->isArgument() && (!var->isLocal() || var->isStatic()))
Packit 2035a7
                continue;
Packit 2035a7
Packit 2035a7
            // Don't check reference variables
Packit 2035a7
            if (var && var->isReference())
Packit 2035a7
                continue;
Packit 2035a7
Packit 2035a7
            // non-pod variable
Packit 2035a7
            if (_tokenizer->isCPP()) {
Packit 2035a7
                if (!var)
Packit 2035a7
                    continue;
Packit 2035a7
                // Possibly automatically deallocated memory
Packit 2035a7
                if (!var->typeStartToken()->isStandardType() && Token::Match(varTok, "%var% = new"))
Packit 2035a7
                    continue;
Packit 2035a7
                if (!var->isPointer() && !var->typeStartToken()->isStandardType())
Packit 2035a7
                    continue;
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            // allocation?
Packit 2035a7
            if (varTok->next()->astOperand2() && Token::Match(varTok->next()->astOperand2()->previous(), "%type% (")) {
Packit 2035a7
                const Library::AllocFunc* f = _settings->library.alloc(varTok->next()->astOperand2()->previous());
Packit 2035a7
                if (f && f->arg == -1) {
Packit 2035a7
                    VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
Packit 2035a7
                    varAlloc.type = f->groupId;
Packit 2035a7
                    varAlloc.status = VarInfo::ALLOC;
Packit 2035a7
                }
Packit 2035a7
            } else if (_tokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) {
Packit 2035a7
                const Token* tok2 = varTok->tokAt(2)->astOperand1();
Packit 2035a7
                bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
Packit 2035a7
                VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
Packit 2035a7
                varAlloc.type = arrayNew ? -2 : -1;
Packit 2035a7
                varAlloc.status = VarInfo::ALLOC;
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            // Assigning non-zero value variable. It might be used to
Packit 2035a7
            // track the execution for a later if condition.
Packit 2035a7
            if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toLongNumber(varTok->strAt(2)) != 0)
Packit 2035a7
                notzero.insert(varTok->varId());
Packit 2035a7
            else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName())
Packit 2035a7
                notzero.insert(varTok->varId());
Packit 2035a7
            else
Packit 2035a7
                notzero.erase(varTok->varId());
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // if/else
Packit 2035a7
        else if (Token::simpleMatch(tok, "if (")) {
Packit 2035a7
            // Parse function calls inside the condition
Packit 2035a7
            for (const Token *innerTok = tok->tokAt(2); innerTok; innerTok = innerTok->next()) {
Packit 2035a7
                if (Token::Match(innerTok, "%var% =")) {
Packit 2035a7
                    // allocation?
Packit 2035a7
                    if (Token::Match(innerTok->tokAt(2), "%type% (")) {
Packit 2035a7
                        const Library::AllocFunc* f = _settings->library.alloc(innerTok->tokAt(2));
Packit 2035a7
                        if (f && f->arg == -1) {
Packit 2035a7
                            VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
Packit 2035a7
                            varAlloc.type = f->groupId;
Packit 2035a7
                            varAlloc.status = VarInfo::ALLOC;
Packit 2035a7
                        }
Packit 2035a7
                    } else if (_tokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) {
Packit 2035a7
                        const Token* tok2 = innerTok->tokAt(2)->astOperand1();
Packit 2035a7
                        bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
Packit 2035a7
                        VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
Packit 2035a7
                        varAlloc.type = arrayNew ? -2 : -1;
Packit 2035a7
                        varAlloc.status = VarInfo::ALLOC;
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                if (innerTok->str() == ")")
Packit 2035a7
                    break;
Packit 2035a7
                if (innerTok->str() == "(" && innerTok->previous()->isName()) {
Packit 2035a7
                    VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
Packit 2035a7
                    functionCall(innerTok->previous(), varInfo, allocation, nullptr);
Packit 2035a7
                    innerTok = innerTok->link();
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            const Token *tok2 = tok->linkAt(1);
Packit 2035a7
            if (Token::simpleMatch(tok2, ") {")) {
Packit 2035a7
                VarInfo varInfo1(*varInfo);  // VarInfo for if code
Packit 2035a7
                VarInfo varInfo2(*varInfo);  // VarInfo for else code
Packit 2035a7
Packit 2035a7
                // Recursively scan variable comparisons in condition
Packit 2035a7
                std::stack<const Token *> tokens;
Packit 2035a7
                tokens.push(tok->next()->astOperand2());
Packit 2035a7
                while (!tokens.empty()) {
Packit 2035a7
                    const Token *tok3 = tokens.top();
Packit 2035a7
                    tokens.pop();
Packit 2035a7
                    if (!tok3)
Packit 2035a7
                        continue;
Packit 2035a7
                    if (tok3->str() == "&&") {
Packit 2035a7
                        tokens.push(tok3->astOperand1());
Packit 2035a7
                        tokens.push(tok3->astOperand2());
Packit 2035a7
                        continue;
Packit 2035a7
                    }
Packit 2035a7
                    if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
Packit 2035a7
                        tokens.push(tok3->astOperand2());
Packit 2035a7
                        continue;
Packit 2035a7
                    } else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) {
Packit 2035a7
                        const std::vector<const Token *> params = getArguments(tok3->previous());
Packit 2035a7
                        for (unsigned int i = 0; i < params.size(); ++i) {
Packit 2035a7
                            const Token *par = params[i];
Packit 2035a7
                            if (!par->isComparisonOp())
Packit 2035a7
                                continue;
Packit 2035a7
                            const Token *vartok = nullptr;
Packit 2035a7
                            if (astIsVariableComparison(par, "!=", "0", &vartok) ||
Packit 2035a7
                                astIsVariableComparison(par, "==", "0", &vartok) ||
Packit 2035a7
                                astIsVariableComparison(par, "<", "0", &vartok) ||
Packit 2035a7
                                astIsVariableComparison(par, ">", "0", &vartok) ||
Packit 2035a7
                                astIsVariableComparison(par, "==", "-1", &vartok) ||
Packit 2035a7
                                astIsVariableComparison(par, "!=", "-1", &vartok)) {
Packit 2035a7
                                varInfo1.erase(vartok->varId());
Packit 2035a7
                                varInfo2.erase(vartok->varId());
Packit 2035a7
                            }
Packit 2035a7
                        }
Packit 2035a7
                        continue;
Packit 2035a7
                    }
Packit 2035a7
Packit 2035a7
                    const Token *vartok = nullptr;
Packit 2035a7
                    if (astIsVariableComparison(tok3, "!=", "0", &vartok)) {
Packit 2035a7
                        varInfo2.erase(vartok->varId());
Packit 2035a7
                        if (notzero.find(vartok->varId()) != notzero.end())
Packit 2035a7
                            varInfo2.clear();
Packit 2035a7
                    } else if (astIsVariableComparison(tok3, "==", "0", &vartok)) {
Packit 2035a7
                        varInfo1.erase(vartok->varId());
Packit 2035a7
                    } else if (astIsVariableComparison(tok3, "<", "0", &vartok)) {
Packit 2035a7
                        varInfo1.erase(vartok->varId());
Packit 2035a7
                    } else if (astIsVariableComparison(tok3, ">", "0", &vartok)) {
Packit 2035a7
                        varInfo2.erase(vartok->varId());
Packit 2035a7
                    } else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) {
Packit 2035a7
                        varInfo1.erase(vartok->varId());
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                checkScope(tok2->next(), &varInfo1, notzero);
Packit 2035a7
                tok2 = tok2->linkAt(1);
Packit 2035a7
                if (Token::simpleMatch(tok2, "} else {")) {
Packit 2035a7
                    checkScope(tok2->tokAt(2), &varInfo2, notzero);
Packit 2035a7
                    tok = tok2->linkAt(2)->previous();
Packit 2035a7
                } else {
Packit 2035a7
                    tok = tok2->previous();
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                VarInfo old;
Packit 2035a7
                old.swap(*varInfo);
Packit 2035a7
Packit 2035a7
                std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it;
Packit 2035a7
Packit 2035a7
                for (it = old.alloctype.begin(); it != old.alloctype.end(); ++it) {
Packit 2035a7
                    const unsigned int varId = it->first;
Packit 2035a7
                    if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end())
Packit 2035a7
                        continue;
Packit 2035a7
                    if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() ||
Packit 2035a7
                        varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) {
Packit 2035a7
                        varInfo1.erase(varId);
Packit 2035a7
                        varInfo2.erase(varId);
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                // Conditional allocation in varInfo1
Packit 2035a7
                for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
Packit 2035a7
                    if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
Packit 2035a7
                        old.alloctype.find(it->first) == old.alloctype.end()) {
Packit 2035a7
                        varInfo->conditionalAlloc.insert(it->first);
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                // Conditional allocation in varInfo2
Packit 2035a7
                for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
Packit 2035a7
                    if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() &&
Packit 2035a7
                        old.alloctype.find(it->first) == old.alloctype.end()) {
Packit 2035a7
                        varInfo->conditionalAlloc.insert(it->first);
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                // Conditional allocation/deallocation
Packit 2035a7
                for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
Packit 2035a7
                    if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
Packit 2035a7
                        varInfo->conditionalAlloc.erase(it->first);
Packit 2035a7
                        varInfo2.erase(it->first);
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
                for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
Packit 2035a7
                    if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
Packit 2035a7
                        varInfo->conditionalAlloc.erase(it->first);
Packit 2035a7
                        varInfo1.erase(it->first);
Packit 2035a7
                    }
Packit 2035a7
                }
Packit 2035a7
Packit 2035a7
                alloctype.insert(varInfo1.alloctype.begin(), varInfo1.alloctype.end());
Packit 2035a7
                alloctype.insert(varInfo2.alloctype.begin(), varInfo2.alloctype.end());
Packit 2035a7
Packit 2035a7
                possibleUsage.insert(varInfo1.possibleUsage.begin(), varInfo1.possibleUsage.end());
Packit 2035a7
                possibleUsage.insert(varInfo2.possibleUsage.begin(), varInfo2.possibleUsage.end());
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // unknown control.. (TODO: handle loops)
Packit 2035a7
        else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) {
Packit 2035a7
            varInfo->clear();
Packit 2035a7
            break;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // return
Packit 2035a7
        else if (tok->str() == "return") {
Packit 2035a7
            ret(tok, *varInfo);
Packit 2035a7
            varInfo->clear();
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // throw
Packit 2035a7
        else if (_tokenizer->isCPP() && tok->str() == "throw") {
Packit 2035a7
            bool tryFound = false;
Packit 2035a7
            const Scope* scope = tok->scope();
Packit 2035a7
            while (scope && scope->isExecutable()) {
Packit 2035a7
                if (scope->type == Scope::eTry)
Packit 2035a7
                    tryFound = true;
Packit 2035a7
                scope = scope->nestedIn;
Packit 2035a7
            }
Packit 2035a7
            // If the execution leaves the function then treat it as return
Packit 2035a7
            if (!tryFound)
Packit 2035a7
                ret(tok, *varInfo);
Packit 2035a7
            varInfo->clear();
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // Function call..
Packit 2035a7
        else if (Token::Match(ftok, "%type% (")) {
Packit 2035a7
            const Library::AllocFunc* af = _settings->library.dealloc(ftok);
Packit 2035a7
            VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
Packit 2035a7
            if (allocation.type == 0)
Packit 2035a7
                allocation.status = VarInfo::NOALLOC;
Packit 2035a7
            functionCall(ftok, varInfo, allocation, af);
Packit 2035a7
Packit 2035a7
            tok = ftok->next()->link();
Packit 2035a7
Packit 2035a7
            // Handle scopes that might be noreturn
Packit 2035a7
            if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) {
Packit 2035a7
                const std::string &functionName(tok->link()->previous()->str());
Packit 2035a7
                bool unknown = false;
Packit 2035a7
                if (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
Packit 2035a7
                    if (!unknown)
Packit 2035a7
                        varInfo->clear();
Packit 2035a7
                    else if (!_settings->library.isLeakIgnore(functionName) && !_settings->library.isUse(functionName))
Packit 2035a7
                        varInfo->possibleUsageAll(functionName);
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            continue;
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // delete
Packit 2035a7
        else if (_tokenizer->isCPP() && tok->str() == "delete") {
Packit 2035a7
            bool arrayDelete = (tok->strAt(1) == "[");
Packit 2035a7
            if (arrayDelete)
Packit 2035a7
                tok = tok->tokAt(3);
Packit 2035a7
            else
Packit 2035a7
                tok = tok->next();
Packit 2035a7
            while (Token::Match(tok, "%name% ::|."))
Packit 2035a7
                tok = tok->tokAt(2);
Packit 2035a7
            const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0;
Packit 2035a7
            if (!isnull && tok->varId() && tok->strAt(1) != "[") {
Packit 2035a7
                VarInfo::AllocInfo allocation(arrayDelete ? -2 : -1, VarInfo::DEALLOC);
Packit 2035a7
                changeAllocStatus(varInfo, allocation, tok, tok);
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // goto => weird execution path
Packit 2035a7
        else if (tok->str() == "goto") {
Packit 2035a7
            varInfo->clear();
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        // continue/break
Packit 2035a7
        else if (Token::Match(tok, "continue|break ;")) {
Packit 2035a7
            varInfo->clear();
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
Packit 2035a7
{
Packit 2035a7
    std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
Packit 2035a7
    std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
Packit 2035a7
    const std::map<unsigned int, VarInfo::AllocInfo>::iterator var = alloctype.find(arg->varId());
Packit 2035a7
    if (var != alloctype.end()) {
Packit 2035a7
        if (allocation.status == VarInfo::NOALLOC) {
Packit 2035a7
            // possible usage
Packit 2035a7
            possibleUsage[arg->varId()] = tok->str();
Packit 2035a7
            if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
Packit 2035a7
                varInfo->erase(arg->varId());
Packit 2035a7
        } else if (var->second.status == VarInfo::DEALLOC) {
Packit 2035a7
            doubleFreeError(tok, arg->str(), allocation.type);
Packit 2035a7
        } else if (var->second.type != allocation.type) {
Packit 2035a7
            // mismatching allocation and deallocation
Packit 2035a7
            mismatchError(tok, arg->str());
Packit 2035a7
            varInfo->erase(arg->varId());
Packit 2035a7
        } else {
Packit 2035a7
            // deallocation
Packit 2035a7
            var->second.status = VarInfo::DEALLOC;
Packit 2035a7
            var->second.type = allocation.type;
Packit 2035a7
        }
Packit 2035a7
    } else if (allocation.status != VarInfo::NOALLOC) {
Packit 2035a7
        alloctype[arg->varId()].status = VarInfo::DEALLOC;
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af)
Packit 2035a7
{
Packit 2035a7
    // Ignore function call?
Packit 2035a7
    if (_settings->library.isLeakIgnore(tok->str()))
Packit 2035a7
        return;
Packit 2035a7
Packit 2035a7
    int argNr = 1;
Packit 2035a7
    for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
Packit 2035a7
        if (_tokenizer->isCPP() && arg->str() == "new") {
Packit 2035a7
            arg = arg->next();
Packit 2035a7
            if (Token::simpleMatch(arg, "( std :: nothrow )"))
Packit 2035a7
                arg = arg->tokAt(5);
Packit 2035a7
        }
Packit 2035a7
Packit 2035a7
        while (Token::Match(arg, "%var% . %var%"))
Packit 2035a7
            arg = arg->tokAt(2);
Packit 2035a7
Packit 2035a7
        if (Token::Match(arg, "%var% [-,)] !!.") || Token::Match(arg, "& %var%")) {
Packit 2035a7
            // goto variable
Packit 2035a7
            if (arg->str() == "&")
Packit 2035a7
                arg = arg->next();
Packit 2035a7
Packit 2035a7
            bool isnull = arg->hasKnownIntValue() && arg->values().front().intvalue == 0;
Packit 2035a7
Packit 2035a7
            // Is variable allocated?
Packit 2035a7
            if (!isnull && (!af || af->arg == argNr))
Packit 2035a7
                changeAllocStatus(varInfo, allocation, tok, arg);
Packit 2035a7
        } else if (Token::Match(arg, "%name% (")) {
Packit 2035a7
            const Library::AllocFunc* allocFunc = _settings->library.dealloc(arg);
Packit 2035a7
            VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC);
Packit 2035a7
            if (alloc.type == 0)
Packit 2035a7
                alloc.status = VarInfo::NOALLOC;
Packit 2035a7
            functionCall(arg, varInfo, alloc, allocFunc);
Packit 2035a7
        }
Packit 2035a7
        argNr++;
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
Packit 2035a7
                                       const VarInfo &varInfo)
Packit 2035a7
{
Packit 2035a7
    const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
Packit 2035a7
    const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
Packit 2035a7
Packit 2035a7
    const std::map<unsigned int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
Packit 2035a7
    if (var != alloctype.end() && var->second.status != VarInfo::DEALLOC) {
Packit 2035a7
        const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
Packit 2035a7
        if (use == possibleUsage.end()) {
Packit 2035a7
            leakError(vartok, vartok->str(), var->second.type);
Packit 2035a7
        } else {
Packit 2035a7
            configurationInfo(vartok, use->second);
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
Packit 2035a7
{
Packit 2035a7
    const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
Packit 2035a7
    const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
Packit 2035a7
Packit 2035a7
    const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
Packit 2035a7
    for (std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
Packit 2035a7
        // don't warn if variable is conditionally allocated
Packit 2035a7
        if (it->second.status != VarInfo::DEALLOC && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        // don't warn if there is a reference of the variable
Packit 2035a7
        if (varInfo.referenced.find(it->first) != varInfo.referenced.end())
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        const unsigned int varid = it->first;
Packit 2035a7
        const Variable *var = symbolDatabase->getVariableFromVarId(varid);
Packit 2035a7
        if (var) {
Packit 2035a7
            bool used = false;
Packit 2035a7
            for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
Packit 2035a7
                if (tok2->str() == ";")
Packit 2035a7
                    break;
Packit 2035a7
                if (Token::Match(tok2, "return|(|, %varid% [);,]", varid)) {
Packit 2035a7
                    used = true;
Packit 2035a7
                    break;
Packit 2035a7
                }
Packit 2035a7
                if (Token::Match(tok2, "return|(|, & %varid% . %name% [);,]", varid)) {
Packit 2035a7
                    used = true;
Packit 2035a7
                    break;
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
Packit 2035a7
            // return deallocated pointer
Packit 2035a7
            if (used && it->second.status == VarInfo::DEALLOC)
Packit 2035a7
                deallocReturnError(tok, var->name());
Packit 2035a7
Packit 2035a7
            else if (!used && it->second.status != VarInfo::DEALLOC) {
Packit 2035a7
                const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(varid);
Packit 2035a7
                if (use == possibleUsage.end()) {
Packit 2035a7
                    leakError(tok, var->name(), it->second.type);
Packit 2035a7
                } else {
Packit 2035a7
                    configurationInfo(tok, use->second);
Packit 2035a7
                }
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
}