Removed standalone check CheckOther::checkDoubleFree(), integrated (most of) the functionality into CheckLeakAutoVar. Added support for new/delete to checkLeakAutoVar.
This commit is contained in:
parent
70a38b1da6
commit
0488f45a5f
|
@ -21,10 +21,7 @@
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "checkleakautovar.h"
|
#include "checkleakautovar.h"
|
||||||
|
|
||||||
#include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
|
#include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
|
||||||
#include "checkother.h" // <- doubleFreeError
|
|
||||||
|
|
||||||
#include "tokenize.h"
|
#include "tokenize.h"
|
||||||
#include "symboldatabase.h"
|
#include "symboldatabase.h"
|
||||||
|
|
||||||
|
@ -34,9 +31,6 @@
|
||||||
// Register this check class (by creating a static instance of it)
|
// Register this check class (by creating a static instance of it)
|
||||||
namespace {
|
namespace {
|
||||||
CheckLeakAutoVar instance;
|
CheckLeakAutoVar instance;
|
||||||
|
|
||||||
const int DEALLOC = -1;
|
|
||||||
const int NOALLOC = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -44,14 +38,14 @@ namespace {
|
||||||
void VarInfo::print()
|
void VarInfo::print()
|
||||||
{
|
{
|
||||||
std::cout << "size=" << alloctype.size() << std::endl;
|
std::cout << "size=" << alloctype.size() << std::endl;
|
||||||
std::map<unsigned int, int>::const_iterator it;
|
std::map<unsigned int, AllocInfo>::const_iterator it;
|
||||||
for (it = alloctype.begin(); it != alloctype.end(); ++it) {
|
for (it = alloctype.begin(); it != alloctype.end(); ++it) {
|
||||||
std::string strusage;
|
std::string strusage;
|
||||||
std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(it->first);
|
std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(it->first);
|
||||||
if (use != possibleUsage.end())
|
if (use != possibleUsage.end())
|
||||||
strusage = use->second;
|
strusage = use->second;
|
||||||
|
|
||||||
std::cout << "alloctype='" << it->second << "' "
|
std::cout << "alloctype='" << it->second.type << "' "
|
||||||
<< "possibleUsage='" << strusage << "'" << std::endl;
|
<< "possibleUsage='" << strusage << "'" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +53,7 @@ void VarInfo::print()
|
||||||
void VarInfo::possibleUsageAll(const std::string &functionName)
|
void VarInfo::possibleUsageAll(const std::string &functionName)
|
||||||
{
|
{
|
||||||
possibleUsage.clear();
|
possibleUsage.clear();
|
||||||
std::map<unsigned int, int>::const_iterator it;
|
std::map<unsigned int, AllocInfo>::const_iterator it;
|
||||||
for (it = alloctype.begin(); it != alloctype.end(); ++it)
|
for (it = alloctype.begin(); it != alloctype.end(); ++it)
|
||||||
possibleUsage[it->first] = functionName;
|
possibleUsage[it->first] = functionName;
|
||||||
}
|
}
|
||||||
|
@ -102,10 +96,22 @@ void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &fu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
|
||||||
|
{
|
||||||
|
if (_settings->library.isresource(type))
|
||||||
|
reportError(tok, Severity::error, "doubleFree", "Resource handle '" + varname + "' freed twice.");
|
||||||
|
else
|
||||||
|
reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname + "' is freed twice.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CheckLeakAutoVar::check()
|
void CheckLeakAutoVar::check()
|
||||||
{
|
{
|
||||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
|
// Local variables that are known to be non-zero.
|
||||||
|
const std::set<unsigned int> notzero;
|
||||||
|
|
||||||
// Check function scopes
|
// Check function scopes
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
for (std::size_t i = 0; i < functions; ++i) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
@ -113,15 +119,12 @@ void CheckLeakAutoVar::check()
|
||||||
// Empty variable info
|
// Empty variable info
|
||||||
VarInfo varInfo;
|
VarInfo varInfo;
|
||||||
|
|
||||||
// Local variables that are known to be non-zero.
|
|
||||||
const std::set<unsigned int> notzero;
|
|
||||||
|
|
||||||
checkScope(scope->classStart, &varInfo, notzero);
|
checkScope(scope->classStart, &varInfo, notzero);
|
||||||
|
|
||||||
varInfo.conditionalAlloc.clear();
|
varInfo.conditionalAlloc.clear();
|
||||||
|
|
||||||
// Clear reference arguments from varInfo..
|
// Clear reference arguments from varInfo..
|
||||||
std::map<unsigned int, int>::iterator it = varInfo.alloctype.begin();
|
std::map<unsigned int, VarInfo::AllocInfo>::iterator it = varInfo.alloctype.begin();
|
||||||
while (it != varInfo.alloctype.end()) {
|
while (it != varInfo.alloctype.end()) {
|
||||||
const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
|
const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
|
||||||
if (!var ||
|
if (!var ||
|
||||||
|
@ -140,7 +143,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
VarInfo *varInfo,
|
VarInfo *varInfo,
|
||||||
std::set<unsigned int> notzero)
|
std::set<unsigned int> notzero)
|
||||||
{
|
{
|
||||||
std::map<unsigned int, int> &alloctype = varInfo->alloctype;
|
std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
|
||||||
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
||||||
const std::set<unsigned int> conditionalAlloc(varInfo->conditionalAlloc);
|
const std::set<unsigned int> conditionalAlloc(varInfo->conditionalAlloc);
|
||||||
|
|
||||||
|
@ -149,9 +152,9 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
|
for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
|
||||||
// Deallocation and then dereferencing pointer..
|
// Deallocation and then dereferencing pointer..
|
||||||
if (tok->varId() > 0) {
|
if (tok->varId() > 0) {
|
||||||
const std::map<unsigned int, int>::iterator var = alloctype.find(tok->varId());
|
const std::map<unsigned int, VarInfo::AllocInfo>::iterator var = alloctype.find(tok->varId());
|
||||||
if (var != alloctype.end()) {
|
if (var != alloctype.end()) {
|
||||||
if (var->second == DEALLOC && !Token::Match(tok->previous(), "[;{},=] %var% =")) {
|
if (var->second.status == VarInfo::DEALLOC && !Token::Match(tok->previous(), "[;{},=] %var% =")) {
|
||||||
deallocUseError(tok, tok->str());
|
deallocUseError(tok, tok->str());
|
||||||
} else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
|
} else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
|
||||||
varInfo->erase(tok->varId());
|
varInfo->erase(tok->varId());
|
||||||
|
@ -164,7 +167,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok->str() == "(" && tok->previous()->isName()) {
|
if (tok->str() == "(" && tok->previous()->isName()) {
|
||||||
functionCall(tok->previous(), varInfo, NOALLOC);
|
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
|
||||||
|
functionCall(tok->previous(), varInfo, allocation);
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -172,6 +176,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
// look for end of statement
|
// look for end of statement
|
||||||
if (!Token::Match(tok, "[;{}]") || Token::Match(tok->next(), "[;{}]"))
|
if (!Token::Match(tok, "[;{}]") || Token::Match(tok->next(), "[;{}]"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
tok = tok->next();
|
tok = tok->next();
|
||||||
if (!tok || tok == endToken)
|
if (!tok || tok == endToken)
|
||||||
break;
|
break;
|
||||||
|
@ -236,8 +241,12 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
if (Token::Match(tok->tokAt(2), "%type% (")) {
|
if (Token::Match(tok->tokAt(2), "%type% (")) {
|
||||||
int i = _settings->library.alloc(tok->tokAt(2));
|
int i = _settings->library.alloc(tok->tokAt(2));
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
alloctype[tok->varId()] = i;
|
alloctype[tok->varId()].type = i;
|
||||||
|
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
||||||
}
|
}
|
||||||
|
} else if (_tokenizer->isCPP() && tok->strAt(2) == "new") {
|
||||||
|
alloctype[tok->varId()].type = -1;
|
||||||
|
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigning non-zero value variable. It might be used to
|
// Assigning non-zero value variable. It might be used to
|
||||||
|
@ -257,8 +266,10 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
if (innerTok->str() == ")")
|
if (innerTok->str() == ")")
|
||||||
break;
|
break;
|
||||||
if (innerTok->str() == "(" && innerTok->previous()->isName()) {
|
if (innerTok->str() == "(" && innerTok->previous()->isName()) {
|
||||||
const int deallocId = _settings->library.dealloc(tok);
|
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
||||||
functionCall(innerTok->previous(), varInfo, deallocId);
|
if (allocation.type == 0)
|
||||||
|
allocation.status = VarInfo::NOALLOC;
|
||||||
|
functionCall(innerTok->previous(), varInfo, allocation);
|
||||||
innerTok = innerTok->link();
|
innerTok = innerTok->link();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,7 +314,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
old.swap(*varInfo);
|
old.swap(*varInfo);
|
||||||
|
|
||||||
// Conditional allocation in varInfo1
|
// Conditional allocation in varInfo1
|
||||||
std::map<unsigned int, int>::const_iterator it;
|
std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it;
|
||||||
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
||||||
if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
|
if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
|
||||||
old.alloctype.find(it->first) == old.alloctype.end()) {
|
old.alloctype.find(it->first) == old.alloctype.end()) {
|
||||||
|
@ -321,13 +332,13 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
|
|
||||||
// Conditional allocation/deallocation
|
// Conditional allocation/deallocation
|
||||||
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
||||||
if (it->second == DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
||||||
varInfo->conditionalAlloc.erase(it->first);
|
varInfo->conditionalAlloc.erase(it->first);
|
||||||
varInfo2.erase(it->first);
|
varInfo2.erase(it->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
|
for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
|
||||||
if (it->second == DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
||||||
varInfo->conditionalAlloc.erase(it->first);
|
varInfo->conditionalAlloc.erase(it->first);
|
||||||
varInfo1.erase(it->first);
|
varInfo1.erase(it->first);
|
||||||
}
|
}
|
||||||
|
@ -341,22 +352,23 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unknown control..
|
// unknown control.. (TODO: handle loops)
|
||||||
else if (Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) {
|
else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::Match(tok, "do {")) {
|
||||||
varInfo->clear();
|
varInfo->clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function call..
|
// Function call..
|
||||||
else if (Token::Match(tok, "%type% (") && tok->str() != "return") {
|
else if (Token::Match(tok, "%type% (") && tok->str() != "return" && tok->str() != "throw") {
|
||||||
const int dealloc = _settings->library.dealloc(tok);
|
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
||||||
|
if (allocation.type == 0)
|
||||||
functionCall(tok, varInfo, dealloc);
|
allocation.status = VarInfo::NOALLOC;
|
||||||
|
functionCall(tok, varInfo, allocation);
|
||||||
|
|
||||||
tok = tok->next()->link();
|
tok = tok->next()->link();
|
||||||
|
|
||||||
// Handle scopes that might be noreturn
|
// Handle scopes that might be noreturn
|
||||||
if (dealloc == NOALLOC && Token::simpleMatch(tok, ") ; }")) {
|
if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) {
|
||||||
const std::string &functionName(tok->link()->previous()->str());
|
const std::string &functionName(tok->link()->previous()->str());
|
||||||
bool unknown = false;
|
bool unknown = false;
|
||||||
if (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
|
if (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
|
||||||
|
@ -371,6 +383,20 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete
|
||||||
|
else if (_tokenizer->isCPP() && tok->str() == "delete") {
|
||||||
|
if (tok->strAt(1) == "[")
|
||||||
|
tok = tok->tokAt(3);
|
||||||
|
else
|
||||||
|
tok = tok->next();
|
||||||
|
while (Token::Match(tok, "%var% ::|."))
|
||||||
|
tok = tok->tokAt(2);
|
||||||
|
if (tok->varId()) {
|
||||||
|
VarInfo::AllocInfo allocation(-1, VarInfo::DEALLOC);
|
||||||
|
changeAllocStatus(varInfo, allocation, tok, tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// return
|
// return
|
||||||
else if (tok->str() == "return") {
|
else if (tok->str() == "return") {
|
||||||
ret(tok, *varInfo);
|
ret(tok, *varInfo);
|
||||||
|
@ -404,16 +430,40 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const int dealloc)
|
void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
|
||||||
|
{
|
||||||
|
std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
|
||||||
|
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
||||||
|
const std::map<unsigned int, VarInfo::AllocInfo>::iterator var = alloctype.find(arg->varId());
|
||||||
|
if (var != alloctype.end()) {
|
||||||
|
if (allocation.status == VarInfo::NOALLOC) {
|
||||||
|
// possible usage
|
||||||
|
possibleUsage[arg->varId()] = tok->str();
|
||||||
|
if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
|
||||||
|
varInfo->erase(arg->varId());
|
||||||
|
} else if (var->second.status == VarInfo::DEALLOC) {
|
||||||
|
doubleFreeError(tok, arg->str(), allocation.type);
|
||||||
|
} else if (var->second.type != allocation.type) {
|
||||||
|
// mismatching allocation and deallocation
|
||||||
|
mismatchError(tok, arg->str());
|
||||||
|
varInfo->erase(arg->varId());
|
||||||
|
} else {
|
||||||
|
// deallocation
|
||||||
|
var->second.status = VarInfo::DEALLOC;
|
||||||
|
var->second.type = allocation.type;
|
||||||
|
}
|
||||||
|
} else if (allocation.status != VarInfo::NOALLOC) {
|
||||||
|
alloctype[arg->varId()].status = VarInfo::DEALLOC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation)
|
||||||
{
|
{
|
||||||
// Ignore function call?
|
// Ignore function call?
|
||||||
const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end());
|
const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end());
|
||||||
if (ignore)
|
if (ignore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::map<unsigned int, int> &alloctype = varInfo->alloctype;
|
|
||||||
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
|
||||||
|
|
||||||
for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
|
for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
|
||||||
if (arg->str() == "new")
|
if (arg->str() == "new")
|
||||||
arg = arg->next();
|
arg = arg->next();
|
||||||
|
@ -426,29 +476,9 @@ void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const in
|
||||||
arg = arg->next();
|
arg = arg->next();
|
||||||
|
|
||||||
// Is variable allocated?
|
// Is variable allocated?
|
||||||
const std::map<unsigned int,int>::iterator var = alloctype.find(arg->varId());
|
changeAllocStatus(varInfo, allocation, tok, arg);
|
||||||
if (var != alloctype.end()) {
|
|
||||||
if (dealloc == NOALLOC) {
|
|
||||||
// possible usage
|
|
||||||
possibleUsage[arg->varId()] = tok->str();
|
|
||||||
if (var->second == DEALLOC && arg->previous()->str() == "&")
|
|
||||||
varInfo->erase(arg->varId());
|
|
||||||
} else if (var->second == DEALLOC) {
|
|
||||||
CheckOther checkOther(_tokenizer, _settings, _errorLogger);
|
|
||||||
checkOther.doubleFreeError(tok, arg->str());
|
|
||||||
} else if (var->second != dealloc) {
|
|
||||||
// mismatching allocation and deallocation
|
|
||||||
mismatchError(tok, arg->str());
|
|
||||||
varInfo->erase(arg->varId());
|
|
||||||
} else {
|
|
||||||
// deallocation
|
|
||||||
var->second = DEALLOC;
|
|
||||||
}
|
|
||||||
} else if (dealloc != NOALLOC) {
|
|
||||||
alloctype[arg->varId()] = DEALLOC;
|
|
||||||
}
|
|
||||||
} else if (Token::Match(arg, "%var% (")) {
|
} else if (Token::Match(arg, "%var% (")) {
|
||||||
functionCall(arg, varInfo, dealloc);
|
functionCall(arg, varInfo, allocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,14 +487,14 @@ void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const in
|
||||||
void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
|
void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
|
||||||
const VarInfo &varInfo)
|
const VarInfo &varInfo)
|
||||||
{
|
{
|
||||||
const std::map<unsigned int, int> &alloctype = varInfo.alloctype;
|
const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
||||||
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
||||||
|
|
||||||
const std::map<unsigned int,int>::const_iterator var = alloctype.find(vartok->varId());
|
const std::map<unsigned int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
|
||||||
if (var != alloctype.end() && var->second != DEALLOC) {
|
if (var != alloctype.end() && var->second.status != VarInfo::DEALLOC) {
|
||||||
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
|
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
|
||||||
if (use == possibleUsage.end()) {
|
if (use == possibleUsage.end()) {
|
||||||
leakError(vartok, vartok->str(), var->second);
|
leakError(vartok, vartok->str(), var->second.type);
|
||||||
} else {
|
} else {
|
||||||
configurationInfo(vartok, use->second);
|
configurationInfo(vartok, use->second);
|
||||||
}
|
}
|
||||||
|
@ -473,13 +503,13 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
|
||||||
|
|
||||||
void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
||||||
{
|
{
|
||||||
const std::map<unsigned int, int> &alloctype = varInfo.alloctype;
|
const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
||||||
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
||||||
|
|
||||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
for (std::map<unsigned int, int>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
|
for (std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
|
||||||
// don't warn if variable is conditionally allocated
|
// don't warn if variable is conditionally allocated
|
||||||
if (it->second != DEALLOC && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
|
if (it->second.status != VarInfo::DEALLOC && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// don't warn if there is a reference of the variable
|
// don't warn if there is a reference of the variable
|
||||||
|
@ -504,14 +534,13 @@ void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return deallocated pointer
|
// return deallocated pointer
|
||||||
if (used && it->second == DEALLOC)
|
if (used && it->second.status == VarInfo::DEALLOC)
|
||||||
deallocReturnError(tok, var->name());
|
deallocReturnError(tok, var->name());
|
||||||
|
|
||||||
else if (!used && it->second != DEALLOC) {
|
else if (!used && it->second.status != VarInfo::DEALLOC) {
|
||||||
|
|
||||||
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(varid);
|
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(varid);
|
||||||
if (use == possibleUsage.end()) {
|
if (use == possibleUsage.end()) {
|
||||||
leakError(tok, var->name(), it->second);
|
leakError(tok, var->name(), it->second.type);
|
||||||
} else {
|
} else {
|
||||||
configurationInfo(tok, use->second);
|
configurationInfo(tok, use->second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,13 @@
|
||||||
|
|
||||||
class CPPCHECKLIB VarInfo {
|
class CPPCHECKLIB VarInfo {
|
||||||
public:
|
public:
|
||||||
std::map<unsigned int, int> alloctype;
|
enum AllocStatus { DEALLOC = -1, NOALLOC = 0, ALLOC = 1 };
|
||||||
|
struct AllocInfo {
|
||||||
|
AllocStatus status;
|
||||||
|
int type;
|
||||||
|
AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC) : status(status_), type(type_) {}
|
||||||
|
};
|
||||||
|
std::map<unsigned int, AllocInfo> alloctype;
|
||||||
std::map<unsigned int, std::string> possibleUsage;
|
std::map<unsigned int, std::string> possibleUsage;
|
||||||
std::set<unsigned int> conditionalAlloc;
|
std::set<unsigned int> conditionalAlloc;
|
||||||
std::set<unsigned int> referenced;
|
std::set<unsigned int> referenced;
|
||||||
|
@ -98,7 +104,10 @@ private:
|
||||||
std::set<unsigned int> notzero);
|
std::set<unsigned int> notzero);
|
||||||
|
|
||||||
/** parse function call */
|
/** parse function call */
|
||||||
void functionCall(const Token *tok, VarInfo *varInfo, const int dealloc);
|
void functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation);
|
||||||
|
|
||||||
|
/** parse changes in allocation status */
|
||||||
|
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);
|
||||||
|
|
||||||
/** return. either "return" or end of variable scope is seen */
|
/** return. either "return" or end of variable scope is seen */
|
||||||
void ret(const Token *tok, const VarInfo &varInfo);
|
void ret(const Token *tok, const VarInfo &varInfo);
|
||||||
|
@ -110,6 +119,7 @@ private:
|
||||||
void mismatchError(const Token* tok, const std::string &varname);
|
void mismatchError(const Token* tok, const std::string &varname);
|
||||||
void deallocUseError(const Token *tok, const std::string &varname);
|
void deallocUseError(const Token *tok, const std::string &varname);
|
||||||
void deallocReturnError(const Token *tok, const std::string &varname);
|
void deallocReturnError(const Token *tok, const std::string &varname);
|
||||||
|
void doubleFreeError(const Token *tok, const std::string &varname, int type);
|
||||||
|
|
||||||
/** message: user configuration is needed to complete analysis */
|
/** message: user configuration is needed to complete analysis */
|
||||||
void configurationInfo(const Token* tok, const std::string &functionName);
|
void configurationInfo(const Token* tok, const std::string &functionName);
|
||||||
|
@ -118,6 +128,7 @@ private:
|
||||||
CheckLeakAutoVar c(0, settings, errorLogger);
|
CheckLeakAutoVar c(0, settings, errorLogger);
|
||||||
c.deallocReturnError(0, "p");
|
c.deallocReturnError(0, "p");
|
||||||
c.configurationInfo(0, "f"); // user configuration is needed to complete analysis
|
c.configurationInfo(0, "f"); // user configuration is needed to complete analysis
|
||||||
|
c.doubleFreeError(0, "varname", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
@ -125,7 +136,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string classInfo() const {
|
std::string classInfo() const {
|
||||||
return "Detect when a auto variable is allocated but not deallocated.\n";
|
return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -2127,122 +2127,6 @@ void CheckOther::invalidFreeError(const Token *tok, bool inconclusive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Check for double free
|
|
||||||
// free(p); free(p);
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void CheckOther::checkDoubleFree()
|
|
||||||
{
|
|
||||||
std::set<unsigned int> freedVariables;
|
|
||||||
std::set<unsigned int> closeDirVariables;
|
|
||||||
|
|
||||||
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
||||||
for (std::size_t i = 0; i < functions; ++i) {
|
|
||||||
freedVariables.clear();
|
|
||||||
closeDirVariables.clear();
|
|
||||||
const Scope * scope = symbolDatabase->functionScopes[i];
|
|
||||||
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
|
||||||
// Keep track of any variables passed to "free()", "g_free()" or "closedir()",
|
|
||||||
// and report an error if the same variable is passed twice.
|
|
||||||
if (Token::Match(tok, "free|g_free|closedir ( %var% )")) {
|
|
||||||
const unsigned int varId = tok->tokAt(2)->varId();
|
|
||||||
if (varId) {
|
|
||||||
if (Token::Match(tok, "free|g_free")) {
|
|
||||||
if (freedVariables.find(varId) != freedVariables.end())
|
|
||||||
doubleFreeError(tok, tok->strAt(2));
|
|
||||||
else
|
|
||||||
freedVariables.insert(varId);
|
|
||||||
} else if (tok->str() == "closedir") {
|
|
||||||
if (closeDirVariables.find(varId) != closeDirVariables.end())
|
|
||||||
doubleCloseDirError(tok, tok->strAt(2));
|
|
||||||
else
|
|
||||||
closeDirVariables.insert(varId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of any variables operated on by "delete" or "delete[]"
|
|
||||||
// and report an error if the same variable is delete'd twice.
|
|
||||||
else if (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) {
|
|
||||||
const int varIndex = (tok->strAt(1) == "[") ? 3 : 1;
|
|
||||||
const unsigned int varId = tok->tokAt(varIndex)->varId();
|
|
||||||
if (varId) {
|
|
||||||
if (freedVariables.find(varId) != freedVariables.end())
|
|
||||||
doubleFreeError(tok, tok->strAt(varIndex));
|
|
||||||
else
|
|
||||||
freedVariables.insert(varId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this scope doesn't return, clear the set of previously freed variables
|
|
||||||
else if (tok->str() == "}" && _tokenizer->IsScopeNoReturn(tok)) {
|
|
||||||
freedVariables.clear();
|
|
||||||
closeDirVariables.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this scope is a "for" or "while" loop that contains "break" or "continue",
|
|
||||||
// give up on trying to figure out the flow of execution and just clear the set
|
|
||||||
// of previously freed variables.
|
|
||||||
// TODO: There are false negatives. This bailout is only needed when the
|
|
||||||
// loop will exit without free()'ing the memory on the last iteration.
|
|
||||||
else if (tok->str() == "}" && tok->link() && tok->link()->previous() &&
|
|
||||||
tok->link()->linkAt(-1) &&
|
|
||||||
Token::Match(tok->link()->linkAt(-1)->previous(), "while|for") &&
|
|
||||||
Token::findmatch(tok->link()->linkAt(-1), "break|continue ;", tok) != nullptr) {
|
|
||||||
freedVariables.clear();
|
|
||||||
closeDirVariables.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a variable is passed to a function, remove it from the set of previously freed variables
|
|
||||||
else if (Token::Match(tok, "%var% (") && _settings->library.leakignore.find(tok->str()) == _settings->library.leakignore.end()) {
|
|
||||||
|
|
||||||
// If this is a new function definition, clear all variables
|
|
||||||
if (Token::simpleMatch(tok->next()->link(), ") {")) {
|
|
||||||
freedVariables.clear();
|
|
||||||
closeDirVariables.clear();
|
|
||||||
}
|
|
||||||
// If it is a function call, then clear those variables in its argument list
|
|
||||||
else if (Token::simpleMatch(tok->next()->link(), ") ;")) {
|
|
||||||
for (const Token* tok2 = tok->tokAt(2); tok2 != tok->linkAt(1); tok2 = tok2->next()) {
|
|
||||||
const unsigned int varId = tok2->varId();
|
|
||||||
if (varId) {
|
|
||||||
freedVariables.erase(varId);
|
|
||||||
closeDirVariables.erase(varId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a pointer is assigned a new value, remove it from the set of previously freed variables
|
|
||||||
else if (Token::Match(tok, "%var% =")) {
|
|
||||||
const unsigned int varId = tok->varId();
|
|
||||||
if (varId) {
|
|
||||||
freedVariables.erase(varId);
|
|
||||||
closeDirVariables.erase(varId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any control statements in-between delete, free() or closedir() statements
|
|
||||||
// makes it unclear whether any subsequent statements would be redundant.
|
|
||||||
if (Token::Match(tok, "if|else|for|while|break|continue|goto|return|throw|switch")) {
|
|
||||||
freedVariables.clear();
|
|
||||||
closeDirVariables.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOther::doubleFreeError(const Token *tok, const std::string &varname)
|
|
||||||
{
|
|
||||||
reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname +"' is freed twice.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOther::doubleCloseDirError(const Token *tok, const std::string &varname)
|
|
||||||
{
|
|
||||||
reportError(tok, Severity::error, "doubleCloseDir", "Directory handle '" + varname +"' closed twice.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// check for the same expression on both sides of an operator
|
// check for the same expression on both sides of an operator
|
||||||
// (x == x), (x && x), (x || x)
|
// (x == x), (x && x), (x || x)
|
||||||
|
|
|
@ -103,7 +103,6 @@ public:
|
||||||
checkOther.checkPipeParameterSize();
|
checkOther.checkPipeParameterSize();
|
||||||
|
|
||||||
checkOther.checkInvalidFree();
|
checkOther.checkInvalidFree();
|
||||||
checkOther.checkDoubleFree();
|
|
||||||
checkOther.checkRedundantCopy();
|
checkOther.checkRedundantCopy();
|
||||||
checkOther.checkNegativeBitwiseShift();
|
checkOther.checkNegativeBitwiseShift();
|
||||||
checkOther.checkSuspiciousEqualityComparison();
|
checkOther.checkSuspiciousEqualityComparison();
|
||||||
|
@ -209,10 +208,6 @@ public:
|
||||||
void checkInvalidFree();
|
void checkInvalidFree();
|
||||||
void invalidFreeError(const Token *tok, bool inconclusive);
|
void invalidFreeError(const Token *tok, bool inconclusive);
|
||||||
|
|
||||||
/** @brief %Check for double free or double close operations */
|
|
||||||
void checkDoubleFree();
|
|
||||||
void doubleFreeError(const Token *tok, const std::string &varname);
|
|
||||||
|
|
||||||
/** @brief %Check for code creating redundant copies */
|
/** @brief %Check for code creating redundant copies */
|
||||||
void checkRedundantCopy();
|
void checkRedundantCopy();
|
||||||
|
|
||||||
|
@ -291,7 +286,6 @@ private:
|
||||||
void unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive);
|
void unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive);
|
||||||
void pointerPositiveError(const Token *tok, bool inconclusive);
|
void pointerPositiveError(const Token *tok, bool inconclusive);
|
||||||
void SuspiciousSemicolonError(const Token *tok);
|
void SuspiciousSemicolonError(const Token *tok);
|
||||||
void doubleCloseDirError(const Token *tok, const std::string &varname);
|
|
||||||
void negativeBitwiseShiftError(const Token *tok);
|
void negativeBitwiseShiftError(const Token *tok);
|
||||||
void redundantCopyError(const Token *tok, const std::string &varname);
|
void redundantCopyError(const Token *tok, const std::string &varname);
|
||||||
void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean);
|
void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean);
|
||||||
|
@ -309,7 +303,6 @@ private:
|
||||||
c.zerodivError(0, false);
|
c.zerodivError(0, false);
|
||||||
c.zerodivcondError(0,0,false);
|
c.zerodivcondError(0,0,false);
|
||||||
c.misusedScopeObjectError(NULL, "varname");
|
c.misusedScopeObjectError(NULL, "varname");
|
||||||
c.doubleFreeError(0, "varname");
|
|
||||||
c.invalidPointerCastError(0, "float", "double", false);
|
c.invalidPointerCastError(0, "float", "double", false);
|
||||||
c.negativeBitwiseShiftError(0);
|
c.negativeBitwiseShiftError(0);
|
||||||
c.checkPipeParameterSizeError(0, "varname", "dimension");
|
c.checkPipeParameterSizeError(0, "varname", "dimension");
|
||||||
|
@ -371,7 +364,6 @@ private:
|
||||||
"- scoped object destroyed immediately after construction\n"
|
"- scoped object destroyed immediately after construction\n"
|
||||||
"- assignment in an assert statement\n"
|
"- assignment in an assert statement\n"
|
||||||
"- free() or delete of an invalid memory location\n"
|
"- free() or delete of an invalid memory location\n"
|
||||||
"- double free() or double closedir()\n"
|
|
||||||
"- bitwise operation with negative right operand\n"
|
"- bitwise operation with negative right operand\n"
|
||||||
"- provide wrong dimensioned array to pipe() system command (--std=posix)\n"
|
"- provide wrong dimensioned array to pipe() system command (--std=posix)\n"
|
||||||
"- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n"
|
"- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n"
|
||||||
|
|
|
@ -113,7 +113,7 @@ private:
|
||||||
TEST_CASE(nestedAllocation);
|
TEST_CASE(nestedAllocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[]) {
|
void check(const char code[], bool cpp = false) {
|
||||||
// Clear the error buffer..
|
// Clear the error buffer..
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
|
||||||
|
@ -121,39 +121,14 @@ private:
|
||||||
Settings settings;
|
Settings settings;
|
||||||
int id = 0;
|
int id = 0;
|
||||||
while (!settings.library.ismemory(++id));
|
while (!settings.library.ismemory(++id));
|
||||||
settings.library.setalloc("malloc",id);
|
settings.library.setalloc("malloc", id);
|
||||||
settings.library.setdealloc("free",id);
|
settings.library.setdealloc("free", id);
|
||||||
while (!settings.library.isresource(++id));
|
while (!settings.library.isresource(++id));
|
||||||
settings.library.setalloc("fopen",id);
|
settings.library.setalloc("fopen", id);
|
||||||
settings.library.setdealloc("fclose",id);
|
settings.library.setdealloc("fclose", id);
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
std::istringstream istr(code);
|
std::istringstream istr(code);
|
||||||
tokenizer.tokenize(istr, "test.c");
|
tokenizer.tokenize(istr, cpp?"test.cpp":"test.c");
|
||||||
tokenizer.simplifyTokenList2();
|
|
||||||
|
|
||||||
// Check for leaks..
|
|
||||||
CheckLeakAutoVar c;
|
|
||||||
settings.checkLibrary = true;
|
|
||||||
settings.addEnabled("information");
|
|
||||||
c.runSimplifiedChecks(&tokenizer, &settings, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkcpp(const char code[]) {
|
|
||||||
// Clear the error buffer..
|
|
||||||
errout.str("");
|
|
||||||
|
|
||||||
// Tokenize..
|
|
||||||
Settings settings;
|
|
||||||
int id = 0;
|
|
||||||
while (!settings.library.ismemory(++id));
|
|
||||||
settings.library.setalloc("malloc",id);
|
|
||||||
settings.library.setdealloc("free",id);
|
|
||||||
while (!settings.library.isresource(++id));
|
|
||||||
settings.library.setalloc("fopen",id);
|
|
||||||
settings.library.setdealloc("fclose",id);
|
|
||||||
Tokenizer tokenizer(&settings, this);
|
|
||||||
std::istringstream istr(code);
|
|
||||||
tokenizer.tokenize(istr, "test.c.cpp");
|
|
||||||
tokenizer.simplifyTokenList2();
|
tokenizer.simplifyTokenList2();
|
||||||
|
|
||||||
// Check for leaks..
|
// Check for leaks..
|
||||||
|
@ -349,6 +324,391 @@ private:
|
||||||
" free(p);\n"
|
" free(p);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
ASSERT_EQUALS("[test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p, char *r) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" free(r);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo() {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" free(r);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" if (x < 3) free(p);\n"
|
||||||
|
" else { if (x > 9) free(p); }\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" getNext(&p);\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" bar();\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" printf(\"Freed memory at location %x\", p);\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(FILE *p) {\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(FILE *p, FILE *r) {\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
" fclose(r);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(FILE *p) {\n"
|
||||||
|
" if (x < 3) fclose(p);\n"
|
||||||
|
" else { if (x > 9) fclose(p); }\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(FILE *p) {\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
" gethandle(&p);\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(FILE *p) {\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
" gethandle();\n"
|
||||||
|
" fclose(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(Data* p) {\n"
|
||||||
|
" free(p->a);\n"
|
||||||
|
" free(p->b);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void f() {\n"
|
||||||
|
" char *p; p = malloc(100);\n"
|
||||||
|
" if (x) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" exit();\n"
|
||||||
|
" }\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void f() {\n"
|
||||||
|
" char *p; p = malloc(100);\n"
|
||||||
|
" if (x) {\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" x = 0;\n"
|
||||||
|
" }\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void f() {\n"
|
||||||
|
" char *p; p = do_something();\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" p = do_something();\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete p;\n"
|
||||||
|
" delete p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p, char *r) {\n"
|
||||||
|
" delete p;\n"
|
||||||
|
" delete r;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(P p) {\n"
|
||||||
|
" delete p.x;\n"
|
||||||
|
" delete p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete p;\n"
|
||||||
|
" getNext(&p);\n"
|
||||||
|
" delete p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete p;\n"
|
||||||
|
" bar();\n"
|
||||||
|
" delete p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p, char *r) {\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
" delete[] r;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
" getNext(&p);\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(char *p) {\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
" bar();\n"
|
||||||
|
" delete[] p;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"~LineMarker() {\n"
|
||||||
|
" delete pxpm;\n"
|
||||||
|
"}\n"
|
||||||
|
"LineMarker &operator=(const LineMarker &) {\n"
|
||||||
|
" delete pxpm;\n"
|
||||||
|
" pxpm = NULL;\n"
|
||||||
|
" return *this;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" int* ptr; ptr = NULL;\n"
|
||||||
|
" try\n"
|
||||||
|
" {\n"
|
||||||
|
" ptr = new int(4);\n"
|
||||||
|
" }\n"
|
||||||
|
" catch(...)\n"
|
||||||
|
" {\n"
|
||||||
|
" delete ptr;\n"
|
||||||
|
" throw;\n"
|
||||||
|
" }\n"
|
||||||
|
" delete ptr;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"int foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" int* a; a = new int;\n"
|
||||||
|
" bool doDelete; doDelete = true;\n"
|
||||||
|
" if (a != 0)\n"
|
||||||
|
" {\n"
|
||||||
|
" doDelete = false;\n"
|
||||||
|
" delete a;\n"
|
||||||
|
" }\n"
|
||||||
|
" if(doDelete)\n"
|
||||||
|
" delete a;\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}", true);
|
||||||
|
TODO_ASSERT_EQUALS("", "[test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" while(true) {\n"
|
||||||
|
" x = new char[100];\n"
|
||||||
|
" if (y++ > 100)\n"
|
||||||
|
" break;\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" for (int i = 0; i < 10000; i++) {\n"
|
||||||
|
" x = new char[100];\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
"}", true);
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" while (isRunning()) {\n"
|
||||||
|
" x = new char[100];\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
"}", true);
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" while (isRunning()) {\n"
|
||||||
|
" x = malloc(100);\n"
|
||||||
|
" free(x);\n"
|
||||||
|
" }\n"
|
||||||
|
" free(x);\n"
|
||||||
|
"}");
|
||||||
|
TODO_ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" for (;;) {\n"
|
||||||
|
" x = new char[100];\n"
|
||||||
|
" if (y++ > 100)\n"
|
||||||
|
" break;\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void foo(int y)\n"
|
||||||
|
"{\n"
|
||||||
|
" char * x; x = NULL;\n"
|
||||||
|
" do {\n"
|
||||||
|
" x = new char[100];\n"
|
||||||
|
" if (y++ > 100)\n"
|
||||||
|
" break;\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
" } while (true);\n"
|
||||||
|
" delete[] x;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void f()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *p; p = 0;\n"
|
||||||
|
" if (x < 100) {\n"
|
||||||
|
" p = malloc(10);\n"
|
||||||
|
" free(p);\n"
|
||||||
|
" }\n"
|
||||||
|
" free(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void MyFunction()\n"
|
||||||
|
"{\n"
|
||||||
|
" char* data; data = new char[100];\n"
|
||||||
|
" try\n"
|
||||||
|
" {\n"
|
||||||
|
" }\n"
|
||||||
|
" catch(err)\n"
|
||||||
|
" {\n"
|
||||||
|
" delete[] data;\n"
|
||||||
|
" MyThrow(err);\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] data;\n"
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
"void MyThrow(err)\n"
|
||||||
|
"{\n"
|
||||||
|
" throw(err);\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check(
|
||||||
|
"void MyFunction()\n"
|
||||||
|
"{\n"
|
||||||
|
" char* data; data = new char[100];\n"
|
||||||
|
" try\n"
|
||||||
|
" {\n"
|
||||||
|
" }\n"
|
||||||
|
" catch(err)\n"
|
||||||
|
" {\n"
|
||||||
|
" delete[] data;\n"
|
||||||
|
" MyExit(err);\n"
|
||||||
|
" }\n"
|
||||||
|
" delete[] data;\n"
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
"void MyExit(err)\n"
|
||||||
|
"{\n"
|
||||||
|
" exit(err);\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check( // #6252
|
||||||
|
"struct Wrapper {\n"
|
||||||
|
" Thing* m_thing;\n"
|
||||||
|
" Wrapper() : m_thing(0) {\n"
|
||||||
|
" }\n"
|
||||||
|
" ~Wrapper() {\n"
|
||||||
|
" delete m_thing;\n"
|
||||||
|
" }\n"
|
||||||
|
" void changeThing() {\n"
|
||||||
|
" delete m_thing;\n"
|
||||||
|
" m_thing = new Thing;\n"
|
||||||
|
" }\n"
|
||||||
|
"};", true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void doublefree2() { // #3891
|
void doublefree2() { // #3891
|
||||||
|
@ -649,10 +1009,10 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void test5() { // unknown type
|
void test5() { // unknown type
|
||||||
checkcpp("void f() { Fred *p = malloc(10); }");
|
check("void f() { Fred *p = malloc(10); }", true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
check("void f() { Fred *p = malloc(10); }");
|
check("void f() { Fred *p = malloc(10); }", false);
|
||||||
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout.str());
|
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,6 @@ private:
|
||||||
TEST_CASE(checkForSuspiciousSemicolon1);
|
TEST_CASE(checkForSuspiciousSemicolon1);
|
||||||
TEST_CASE(checkForSuspiciousSemicolon2);
|
TEST_CASE(checkForSuspiciousSemicolon2);
|
||||||
|
|
||||||
TEST_CASE(checkDoubleFree);
|
|
||||||
TEST_CASE(checkInvalidFree);
|
TEST_CASE(checkInvalidFree);
|
||||||
|
|
||||||
TEST_CASE(checkRedundantCopy);
|
TEST_CASE(checkRedundantCopy);
|
||||||
|
@ -4842,429 +4841,6 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkDoubleFree() {
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p, char *r) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" free(r);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo() {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" free(r);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" if (x < 3) free(p);\n"
|
|
||||||
" else { if (x > 9) free(p); }\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" getNext(&p);\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" bar();\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" printf(\"Freed memory at location %x\", p);\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}", nullptr, false, false, false, true, &settings_std);
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(DIR *p) {\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Directory handle 'p' closed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(DIR *p, DIR *r) {\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
" closedir(r);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(DIR *p) {\n"
|
|
||||||
" if (x < 3) closedir(p);\n"
|
|
||||||
" else { if (x > 9) closedir(p); }\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(DIR *p) {\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
" gethandle(&p);\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(DIR *p) {\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
" gethandle();\n"
|
|
||||||
" closedir(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Directory handle 'p' closed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(Data* p) {\n"
|
|
||||||
" free(p->a);\n"
|
|
||||||
" free(p->b);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void f() {\n"
|
|
||||||
" char *p; p = malloc(100);\n"
|
|
||||||
" if (x) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" exit();\n"
|
|
||||||
" }\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void f() {\n"
|
|
||||||
" char *p; p = malloc(100);\n"
|
|
||||||
" if (x) {\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" x = 0;\n"
|
|
||||||
" }\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void f() {\n"
|
|
||||||
" char *p; p = do_something();\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" p = do_something();\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p, char *r) {\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
" g_free(r);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
" getNext(&p);\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
" bar();\n"
|
|
||||||
" g_free(p);\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete p;\n"
|
|
||||||
" delete p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p, char *r) {\n"
|
|
||||||
" delete p;\n"
|
|
||||||
" delete r;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete p;\n"
|
|
||||||
" getNext(&p);\n"
|
|
||||||
" delete p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete p;\n"
|
|
||||||
" bar();\n"
|
|
||||||
" delete p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p, char *r) {\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
" delete[] r;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
" getNext(&p);\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(char *p) {\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
" bar();\n"
|
|
||||||
" delete[] p;\n"
|
|
||||||
"}");
|
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"~LineMarker() {\n"
|
|
||||||
" delete pxpm;\n"
|
|
||||||
"}\n"
|
|
||||||
"LineMarker &operator=(const LineMarker &) {\n"
|
|
||||||
" delete pxpm;\n"
|
|
||||||
" pxpm = NULL;\n"
|
|
||||||
" return *this;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo()\n"
|
|
||||||
"{\n"
|
|
||||||
" int* ptr; ptr = NULL;\n"
|
|
||||||
" try\n"
|
|
||||||
" {\n"
|
|
||||||
" ptr = new int(4);\n"
|
|
||||||
" }\n"
|
|
||||||
" catch(...)\n"
|
|
||||||
" {\n"
|
|
||||||
" delete ptr;\n"
|
|
||||||
" throw;\n"
|
|
||||||
" }\n"
|
|
||||||
" delete ptr;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"int foo()\n"
|
|
||||||
"{\n"
|
|
||||||
" int* a; a = new int;\n"
|
|
||||||
" bool doDelete; doDelete = true;\n"
|
|
||||||
" if (a != 0)\n"
|
|
||||||
" {\n"
|
|
||||||
" doDelete = false;\n"
|
|
||||||
" delete a;\n"
|
|
||||||
" }\n"
|
|
||||||
" if(doDelete)\n"
|
|
||||||
" delete a;\n"
|
|
||||||
" return 0;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" while(true) {\n"
|
|
||||||
" x = new char[100];\n"
|
|
||||||
" if (y++ > 100)\n"
|
|
||||||
" break;\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" for (int i = 0; i < 10000; i++) {\n"
|
|
||||||
" x = new char[100];\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" while (isRunning()) {\n"
|
|
||||||
" x = new char[100];\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" while (isRunning()) {\n"
|
|
||||||
" x = malloc(100);\n"
|
|
||||||
" free(x);\n"
|
|
||||||
" }\n"
|
|
||||||
" free(x);\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" for (;;) {\n"
|
|
||||||
" x = new char[100];\n"
|
|
||||||
" if (y++ > 100)\n"
|
|
||||||
" break;\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void foo(int y)\n"
|
|
||||||
"{\n"
|
|
||||||
" char * x; x = NULL;\n"
|
|
||||||
" do {\n"
|
|
||||||
" x = new char[100];\n"
|
|
||||||
" if (y++ > 100)\n"
|
|
||||||
" break;\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
" } while (true);\n"
|
|
||||||
" delete[] x;\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void f()\n"
|
|
||||||
"{\n"
|
|
||||||
" char *p; p = 0;\n"
|
|
||||||
" if (x < 100) {\n"
|
|
||||||
" p = malloc(10);\n"
|
|
||||||
" free(p);\n"
|
|
||||||
" }\n"
|
|
||||||
" free(p);\n"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'p' is freed twice.\n", "", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void MyFunction()\n"
|
|
||||||
"{\n"
|
|
||||||
" char* data; data = new char[100];\n"
|
|
||||||
" try\n"
|
|
||||||
" {\n"
|
|
||||||
" }\n"
|
|
||||||
" catch(err)\n"
|
|
||||||
" {\n"
|
|
||||||
" delete[] data;\n"
|
|
||||||
" MyThrow(err);\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] data;\n"
|
|
||||||
"}\n"
|
|
||||||
|
|
||||||
"void MyThrow(err)\n"
|
|
||||||
"{\n"
|
|
||||||
" throw(err);\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check(
|
|
||||||
"void MyFunction()\n"
|
|
||||||
"{\n"
|
|
||||||
" char* data; data = new char[100];\n"
|
|
||||||
" try\n"
|
|
||||||
" {\n"
|
|
||||||
" }\n"
|
|
||||||
" catch(err)\n"
|
|
||||||
" {\n"
|
|
||||||
" delete[] data;\n"
|
|
||||||
" MyExit(err);\n"
|
|
||||||
" }\n"
|
|
||||||
" delete[] data;\n"
|
|
||||||
"}\n"
|
|
||||||
|
|
||||||
"void MyExit(err)\n"
|
|
||||||
"{\n"
|
|
||||||
" exit(err);\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check( // #6252
|
|
||||||
"struct Wrapper {\n"
|
|
||||||
" Thing* m_thing;\n"
|
|
||||||
" Wrapper() : m_thing(0) {\n"
|
|
||||||
" }\n"
|
|
||||||
" ~Wrapper() {\n"
|
|
||||||
" delete m_thing;\n"
|
|
||||||
" }\n"
|
|
||||||
" void changeThing() {\n"
|
|
||||||
" delete m_thing;\n"
|
|
||||||
" m_thing = new Thing;\n"
|
|
||||||
" }\n"
|
|
||||||
"};\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void checkInvalidFree() {
|
void checkInvalidFree() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Reference in New Issue