/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//---------------------------------------------------------------------------
// Leaks when using auto variables
//---------------------------------------------------------------------------
#include "checkleakautovar.h"
#include "astutils.h"
#include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
#include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
#include "mathlib.h"
#include "platform.h"
#include "settings.h"
#include "errortypes.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenize.h"
#include "utils.h"
#include "vfvalue.h"
#include
#include
#include
#include
#include
#include
#include
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckLeakAutoVar instance;
}
static const CWE CWE672(672U);
static const CWE CWE415(415U);
// Hardcoded allocation types (not from library)
static constexpr int NEW_ARRAY = -2;
static constexpr int NEW = -1;
static const std::array, 4> alloc_failed_conds {{{"==", "0"}, {"<", "0"}, {"==", "-1"}, {"<=", "-1"}}};
static const std::array, 4> alloc_success_conds {{{"!=", "0"}, {">", "0"}, {"!=", "-1"}, {">=", "0"}}};
static bool isAutoDeallocType(const Type* type) {
if (!type)
return true;
if (type->classScope && type->classScope->numConstructors == 0 &&
(type->classScope->varlist.empty() || type->needInitialization == Type::NeedInitialization::True) &&
std::none_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [](const Type::BaseInfo& bi) {
return isAutoDeallocType(bi.type);
}))
return false;
return true;
}
/**
* @brief Is variable type some class with automatic deallocation?
* @param var variable token
* @return true unless it can be seen there is no automatic deallocation
*/
static bool isAutoDealloc(const Variable *var)
{
if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE)
return false;
// return false if the type is a simple record type without side effects
// a type that has no side effects (no constructors and no members with constructors)
/** @todo false negative: check constructors for side effects */
return isAutoDeallocType(var->type());
}
template
static bool isVarTokComparison(const Token * tok, const Token ** vartok,
const std::array, N>& ops)
{
return std::any_of(ops.cbegin(), ops.cend(), [&](const std::pair& op) {
return astIsVariableComparison(tok, op.first, op.second, vartok);
});
}
//---------------------------------------------------------------------------
void VarInfo::print()
{
std::cout << "size=" << alloctype.size() << std::endl;
for (std::map::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) {
std::string strusage;
const auto use = possibleUsage.find(it->first);
if (use != possibleUsage.end())
strusage = use->second.first->str();
std::string status;
switch (it->second.status) {
case OWNED:
status = "owned";
break;
case DEALLOC:
status = "dealloc";
break;
case ALLOC:
status = "alloc";
break;
case NOALLOC:
status = "noalloc";
break;
case REALLOC:
status = "realloc";
break;
default:
status = "?";
break;
}
std::cout << "status=" << status << " "
<< "alloctype='" << it->second.type << "' "
<< "possibleUsage='" << strusage << "' "
<< "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " "
<< "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " "
<< "reallocedFrom=" << it->second.reallocedFromType
<< std::endl;
}
}
void VarInfo::possibleUsageAll(const std::pair& functionUsage)
{
possibleUsage.clear();
for (std::map::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it)
possibleUsage[it->first] = functionUsage;
}
void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) const
{
const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings);
if (Library::isresource(type))
checkmemleak.resourceLeakError(tok, varname);
else
checkmemleak.memleakError(tok, varname);
}
void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const
{
const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings);
const std::list callstack = { allocTok, deallocTok };
c.mismatchAllocDealloc(callstack, varname);
}
void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) const
{
const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings);
c.deallocuseError(tok, varname);
}
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname)
{
const std::list locations = { deallocTok, tok };
reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal);
}
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair& functionUsage)
{
if (mSettings->checkLibrary && functionUsage.second == VarInfo::USED &&
(!functionUsage.first || !functionUsage.first->function() || !functionUsage.first->function()->hasBody())) {
const std::string funcStr = functionUsage.first ? mSettings->library.getFunctionName(functionUsage.first) : "f";
reportError(tok,
Severity::information,
"checkLibraryUseIgnore",
"--check-library: Function " + funcStr + "() should have