* Allow to configure realloc like functions
* memleakonrealloc: Bring back tests.
The old memleak checker was removed, and the tests for it was removed in
commit 9765a2dfab
. This also removed the
tests for memleakOnRealloc. Bring back those tests, somewhat modified
since the checker no longer checks for memory leaks.
* Add realloc to mem leak check
* Add tests of realloc buffer size
* Configure realloc functions
* Add test of freopen
* Allow to configure which element is realloc argument
* Fix wrong close in test
cppcheck now warns for this
* Update manual
* Update docs
* Rename alloc/dalloc/realloc functions
Naming the member function realloc caused problems on appveyor. Rename
the alloc and dealloc functions as well for consistency.
* Change comparisson order
* Remove variable and use function call directly
* Create temporary variable to simplify
* Throw mismatchError on mismatching allocation/reallocation
* Refactor to separate function
* Fix potential nullptr dereference
As pointed out by cppcheck.
This commit is contained in:
parent
e0ced1c415
commit
839fcddd8a
|
@ -37,6 +37,21 @@
|
||||||
</optional>
|
</optional>
|
||||||
<ref name="DATA-NAME"/>
|
<ref name="DATA-NAME"/>
|
||||||
</element>
|
</element>
|
||||||
|
<element name="realloc">
|
||||||
|
<optional>
|
||||||
|
<attribute name="init"><ref name="DATA-BOOL"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="arg"><ref name="ARGNO"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="buffer-size"><ref name="DATA-BUFFER-SIZE"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="realloc-arg"><ref name="ARGNO"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<ref name="DATA-NAME"/>
|
||||||
|
</element>
|
||||||
<element name="use"><ref name="DATA-EXTNAME"/></element>
|
<element name="use"><ref name="DATA-EXTNAME"/></element>
|
||||||
</choice>
|
</choice>
|
||||||
</zeroOrMore>
|
</zeroOrMore>
|
||||||
|
@ -62,6 +77,18 @@
|
||||||
</optional>
|
</optional>
|
||||||
<ref name="DATA-NAME"/>
|
<ref name="DATA-NAME"/>
|
||||||
</element>
|
</element>
|
||||||
|
<element name="realloc">
|
||||||
|
<optional>
|
||||||
|
<attribute name="init"><ref name="DATA-BOOL"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="arg"><ref name="ARGNO"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="realloc-arg"><ref name="ARGNO"/></attribute>
|
||||||
|
</optional>
|
||||||
|
<ref name="DATA-NAME"/>
|
||||||
|
</element>
|
||||||
<element name="use"><ref name="DATA-EXTNAME"/></element>
|
<element name="use"><ref name="DATA-EXTNAME"/></element>
|
||||||
</choice>
|
</choice>
|
||||||
</zeroOrMore>
|
</zeroOrMore>
|
||||||
|
|
|
@ -7506,11 +7506,14 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
|
||||||
<alloc init="false" buffer-size="malloc">malloc</alloc>
|
<alloc init="false" buffer-size="malloc">malloc</alloc>
|
||||||
<alloc init="true" buffer-size="calloc">calloc</alloc>
|
<alloc init="true" buffer-size="calloc">calloc</alloc>
|
||||||
<alloc init="false" buffer-size="malloc:2">aligned_alloc</alloc>
|
<alloc init="false" buffer-size="malloc:2">aligned_alloc</alloc>
|
||||||
|
<realloc init="false" buffer-size="malloc:2">realloc</realloc>
|
||||||
|
<realloc init="false" buffer-size="calloc:2,3">reallocarray</realloc>
|
||||||
<dealloc>free</dealloc>
|
<dealloc>free</dealloc>
|
||||||
</memory>
|
</memory>
|
||||||
<resource>
|
<resource>
|
||||||
<alloc init="true">fopen</alloc>
|
<alloc init="true">fopen</alloc>
|
||||||
<alloc init="true">tmpfile</alloc>
|
<alloc init="true">tmpfile</alloc>
|
||||||
|
<realloc init="true" realloc-arg="3">freopen</realloc>
|
||||||
<dealloc>fclose</dealloc>
|
<dealloc>fclose</dealloc>
|
||||||
</resource>
|
</resource>
|
||||||
<container id="stdContainer" endPattern="> !!::" opLessAllowed="false" itEndPattern="> :: iterator|const_iterator|reverse_iterator|const_reverse_iterator">
|
<container id="stdContainer" endPattern="> !!::" opLessAllowed="false" itEndPattern="> :: iterator|const_iterator|reverse_iterator|const_reverse_iterator">
|
||||||
|
|
|
@ -296,7 +296,7 @@ void CheckAutoVariables::autoVariables()
|
||||||
errorAutoVariableAssignment(tok->next(), false);
|
errorAutoVariableAssignment(tok->next(), false);
|
||||||
}
|
}
|
||||||
// Invalid pointer deallocation
|
// Invalid pointer deallocation
|
||||||
else if ((Token::Match(tok, "%name% ( %var% ) ;") && mSettings->library.dealloc(tok)) ||
|
else if ((Token::Match(tok, "%name% ( %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) ||
|
||||||
(mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) {
|
(mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) {
|
||||||
tok = Token::findmatch(tok->next(), "%var%");
|
tok = Token::findmatch(tok->next(), "%var%");
|
||||||
if (isArrayVar(tok))
|
if (isArrayVar(tok))
|
||||||
|
@ -309,7 +309,7 @@ void CheckAutoVariables::autoVariables()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.dealloc(tok)) ||
|
} else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) ||
|
||||||
(mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) {
|
(mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) {
|
||||||
tok = Token::findmatch(tok->next(), "%var%");
|
tok = Token::findmatch(tok->next(), "%var%");
|
||||||
if (isAutoVar(tok))
|
if (isAutoVar(tok))
|
||||||
|
|
|
@ -331,7 +331,7 @@ void CheckClass::copyconstructors()
|
||||||
const Token* tok = func.token->linkAt(1);
|
const Token* tok = func.token->linkAt(1);
|
||||||
for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) {
|
for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) {
|
||||||
if (Token::Match(tok, "%var% ( new") ||
|
if (Token::Match(tok, "%var% ( new") ||
|
||||||
(Token::Match(tok, "%var% ( %name% (") && mSettings->library.alloc(tok->tokAt(2)))) {
|
(Token::Match(tok, "%var% ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) {
|
||||||
const Variable* var = tok->variable();
|
const Variable* var = tok->variable();
|
||||||
if (var && var->isPointer() && var->scope() == scope)
|
if (var && var->isPointer() && var->scope() == scope)
|
||||||
allocatedVars[tok->varId()] = tok;
|
allocatedVars[tok->varId()] = tok;
|
||||||
|
@ -339,7 +339,7 @@ void CheckClass::copyconstructors()
|
||||||
}
|
}
|
||||||
for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) {
|
for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) {
|
||||||
if (Token::Match(tok, "%var% = new") ||
|
if (Token::Match(tok, "%var% = new") ||
|
||||||
(Token::Match(tok, "%var% = %name% (") && mSettings->library.alloc(tok->tokAt(2)))) {
|
(Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) {
|
||||||
const Variable* var = tok->variable();
|
const Variable* var = tok->variable();
|
||||||
if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
|
if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
|
||||||
allocatedVars[tok->varId()] = tok;
|
allocatedVars[tok->varId()] = tok;
|
||||||
|
|
|
@ -335,13 +335,16 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocation?
|
// allocation?
|
||||||
if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
|
const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr;
|
||||||
const Library::AllocFunc* f = mSettings->library.alloc(tokRightAstOperand->previous());
|
if (Token::Match(fTok, "%type% (")) {
|
||||||
|
const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok);
|
||||||
if (f && f->arg == -1) {
|
if (f && f->arg == -1) {
|
||||||
VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
|
VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
|
||||||
varAlloc.type = f->groupId;
|
varAlloc.type = f->groupId;
|
||||||
varAlloc.status = VarInfo::ALLOC;
|
varAlloc.status = VarInfo::ALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeAllocStatusIfRealloc(alloctype, fTok, varTok);
|
||||||
} else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) {
|
} else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) {
|
||||||
const Token* tok2 = varTok->tokAt(2)->astOperand1();
|
const Token* tok2 = varTok->tokAt(2)->astOperand1();
|
||||||
const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
|
const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
|
||||||
|
@ -375,7 +378,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
while (tokRightAstOperand && tokRightAstOperand->isCast())
|
while (tokRightAstOperand && tokRightAstOperand->isCast())
|
||||||
tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
|
tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
|
||||||
if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
|
if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
|
||||||
const Library::AllocFunc* f = mSettings->library.alloc(tokRightAstOperand->previous());
|
const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(tokRightAstOperand->previous());
|
||||||
if (f && f->arg == -1) {
|
if (f && f->arg == -1) {
|
||||||
VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
|
VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
|
||||||
varAlloc.type = f->groupId;
|
varAlloc.type = f->groupId;
|
||||||
|
@ -384,6 +387,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
// Fixme: warn about leak
|
// Fixme: warn about leak
|
||||||
alloctype.erase(innerTok->varId());
|
alloctype.erase(innerTok->varId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeAllocStatusIfRealloc(alloctype, innerTok->tokAt(2), varTok);
|
||||||
} else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) {
|
} else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) {
|
||||||
const Token* tok2 = innerTok->tokAt(2)->astOperand1();
|
const Token* tok2 = innerTok->tokAt(2)->astOperand1();
|
||||||
const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
|
const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
|
||||||
|
@ -552,7 +557,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
// Function call..
|
// Function call..
|
||||||
else if (isFunctionCall(ftok)) {
|
else if (isFunctionCall(ftok)) {
|
||||||
const Token * openingPar = isFunctionCall(ftok);
|
const Token * openingPar = isFunctionCall(ftok);
|
||||||
const Library::AllocFunc* af = mSettings->library.dealloc(ftok);
|
const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok);
|
||||||
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
|
VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC);
|
||||||
if (allocation.type == 0)
|
if (allocation.type == 0)
|
||||||
allocation.status = VarInfo::NOALLOC;
|
allocation.status = VarInfo::NOALLOC;
|
||||||
|
@ -636,7 +641,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
// Check if its a pointer to a function
|
// Check if its a pointer to a function
|
||||||
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
|
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
|
||||||
if (dtok) {
|
if (dtok) {
|
||||||
af = mSettings->library.dealloc(dtok->tokAt(1));
|
af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
|
||||||
} else {
|
} else {
|
||||||
const Token * tscopeStart = nullptr;
|
const Token * tscopeStart = nullptr;
|
||||||
const Token * tscopeEnd = nullptr;
|
const Token * tscopeEnd = nullptr;
|
||||||
|
@ -658,7 +663,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
|
|
||||||
if (tscopeStart && tscopeEnd) {
|
if (tscopeStart && tscopeEnd) {
|
||||||
for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) {
|
for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) {
|
||||||
af = mSettings->library.dealloc(tok2);
|
af = mSettings->library.getDeallocFuncInfo(tok2);
|
||||||
if (af)
|
if (af)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -697,7 +702,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
|
||||||
// check for function call
|
// check for function call
|
||||||
const Token * const openingPar = isFunctionCall(tok);
|
const Token * const openingPar = isFunctionCall(tok);
|
||||||
if (openingPar) {
|
if (openingPar) {
|
||||||
const Library::AllocFunc* allocFunc = mSettings->library.dealloc(tok);
|
const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok);
|
||||||
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC);
|
VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC);
|
||||||
if (alloc.type == 0)
|
if (alloc.type == 0)
|
||||||
alloc.status = VarInfo::NOALLOC;
|
alloc.status = VarInfo::NOALLOC;
|
||||||
|
@ -709,6 +714,22 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map<unsigned int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok)
|
||||||
|
{
|
||||||
|
const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok);
|
||||||
|
if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) {
|
||||||
|
const Token* argTok = getArguments(fTok).at(f->reallocArg - 1);
|
||||||
|
VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()];
|
||||||
|
VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()];
|
||||||
|
if (argAlloc.type != 0 && argAlloc.type != f->groupId)
|
||||||
|
mismatchError(fTok, argTok->str());
|
||||||
|
argAlloc.status = VarInfo::DEALLOC;
|
||||||
|
retAlloc.type = f->groupId;
|
||||||
|
retAlloc.status = VarInfo::ALLOC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
|
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, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
|
||||||
|
@ -740,6 +761,8 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
|
||||||
// Ignore function call?
|
// Ignore function call?
|
||||||
if (mSettings->library.isLeakIgnore(tokName->str()))
|
if (mSettings->library.isLeakIgnore(tokName->str()))
|
||||||
return;
|
return;
|
||||||
|
if (mSettings->library.getReallocFuncInfo(tokName))
|
||||||
|
return;
|
||||||
|
|
||||||
const Token * const tokFirstArg = tokOpeningPar->next();
|
const Token * const tokFirstArg = tokOpeningPar->next();
|
||||||
if (!tokFirstArg || tokFirstArg->str() == ")") {
|
if (!tokFirstArg || tokFirstArg->str() == ")") {
|
||||||
|
@ -796,14 +819,14 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
|
||||||
// Check if its a pointer to a function
|
// Check if its a pointer to a function
|
||||||
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
|
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
|
||||||
if (dtok) {
|
if (dtok) {
|
||||||
sp_af = mSettings->library.dealloc(dtok->tokAt(1));
|
sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
|
||||||
} else {
|
} else {
|
||||||
// If the deleter is a class, check if class calls the dealloc function
|
// If the deleter is a class, check if class calls the dealloc function
|
||||||
dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken);
|
dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken);
|
||||||
if (dtok && dtok->type()) {
|
if (dtok && dtok->type()) {
|
||||||
const Scope * tscope = dtok->type()->classScope;
|
const Scope * tscope = dtok->type()->classScope;
|
||||||
for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) {
|
for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) {
|
||||||
sp_af = mSettings->library.dealloc(tok2);
|
sp_af = mSettings->library.getDeallocFuncInfo(tok2);
|
||||||
if (sp_af)
|
if (sp_af)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,9 @@ private:
|
||||||
/** parse changes in allocation status */
|
/** parse changes in allocation status */
|
||||||
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);
|
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);
|
||||||
|
|
||||||
|
/** update allocation status if reallocation function */
|
||||||
|
void changeAllocStatusIfRealloc(std::map<unsigned int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok);
|
||||||
|
|
||||||
/** 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);
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does tok2 point on a Library allocation function?
|
// Does tok2 point on a Library allocation function?
|
||||||
const int alloctype = mSettings_->library.alloc(tok2, -1);
|
const int alloctype = mSettings_->library.getAllocId(tok2, -1);
|
||||||
if (alloctype > 0) {
|
if (alloctype > 0) {
|
||||||
if (alloctype == mSettings_->library.deallocId("free"))
|
if (alloctype == mSettings_->library.deallocId("free"))
|
||||||
return Malloc;
|
return Malloc;
|
||||||
|
@ -263,7 +263,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does tok point on a Library deallocation function?
|
// Does tok point on a Library deallocation function?
|
||||||
const int dealloctype = mSettings_->library.dealloc(tok, argNr);
|
const int dealloctype = mSettings_->library.getDeallocId(tok, argNr);
|
||||||
if (dealloctype > 0) {
|
if (dealloctype > 0) {
|
||||||
if (dealloctype == mSettings_->library.deallocId("free"))
|
if (dealloctype == mSettings_->library.deallocId("free"))
|
||||||
return Malloc;
|
return Malloc;
|
||||||
|
|
|
@ -188,7 +188,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
||||||
// add alloc/dealloc/use functions..
|
// add alloc/dealloc/use functions..
|
||||||
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
|
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
|
||||||
const std::string memorynodename = memorynode->Name();
|
const std::string memorynodename = memorynode->Name();
|
||||||
if (memorynodename == "alloc") {
|
if (memorynodename == "alloc" || memorynodename == "realloc") {
|
||||||
AllocFunc temp = {0};
|
AllocFunc temp = {0};
|
||||||
temp.groupId = allocationId;
|
temp.groupId = allocationId;
|
||||||
|
|
||||||
|
@ -225,7 +225,18 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
||||||
return Error(BAD_ATTRIBUTE_VALUE, bufferSize);
|
return Error(BAD_ATTRIBUTE_VALUE, bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
mAlloc[memorynode->GetText()] = temp;
|
if (memorynodename == "realloc") {
|
||||||
|
const char *reallocArg = memorynode->Attribute("realloc-arg");
|
||||||
|
if (reallocArg)
|
||||||
|
temp.reallocArg = atoi(reallocArg);
|
||||||
|
else
|
||||||
|
temp.reallocArg = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memorynodename != "realloc")
|
||||||
|
mAlloc[memorynode->GetText()] = temp;
|
||||||
|
else
|
||||||
|
mRealloc[memorynode->GetText()] = temp;
|
||||||
} else if (memorynodename == "dealloc") {
|
} else if (memorynodename == "dealloc") {
|
||||||
AllocFunc temp = {0};
|
AllocFunc temp = {0};
|
||||||
temp.groupId = allocationId;
|
temp.groupId = allocationId;
|
||||||
|
@ -934,30 +945,44 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const
|
||||||
|
|
||||||
|
|
||||||
/** get allocation info for function */
|
/** get allocation info for function */
|
||||||
const Library::AllocFunc* Library::alloc(const Token *tok) const
|
const Library::AllocFunc* Library::getAllocFuncInfo(const Token *tok) const
|
||||||
{
|
{
|
||||||
const std::string funcname = getFunctionName(tok);
|
const std::string funcname = getFunctionName(tok);
|
||||||
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname);
|
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get deallocation info for function */
|
/** get deallocation info for function */
|
||||||
const Library::AllocFunc* Library::dealloc(const Token *tok) const
|
const Library::AllocFunc* Library::getDeallocFuncInfo(const Token *tok) const
|
||||||
{
|
{
|
||||||
const std::string funcname = getFunctionName(tok);
|
const std::string funcname = getFunctionName(tok);
|
||||||
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname);
|
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get allocation id for function */
|
/** get reallocation info for function */
|
||||||
int Library::alloc(const Token *tok, int arg) const
|
const Library::AllocFunc* Library::getReallocFuncInfo(const Token *tok) const
|
||||||
{
|
{
|
||||||
const Library::AllocFunc* af = alloc(tok);
|
const std::string funcname = getFunctionName(tok);
|
||||||
|
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mRealloc, funcname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get allocation id for function */
|
||||||
|
int Library::getAllocId(const Token *tok, int arg) const
|
||||||
|
{
|
||||||
|
const Library::AllocFunc* af = getAllocFuncInfo(tok);
|
||||||
return (af && af->arg == arg) ? af->groupId : 0;
|
return (af && af->arg == arg) ? af->groupId : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get deallocation id for function */
|
/** get deallocation id for function */
|
||||||
int Library::dealloc(const Token *tok, int arg) const
|
int Library::getDeallocId(const Token *tok, int arg) const
|
||||||
{
|
{
|
||||||
const Library::AllocFunc* af = dealloc(tok);
|
const Library::AllocFunc* af = getDeallocFuncInfo(tok);
|
||||||
|
return (af && af->arg == arg) ? af->groupId : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get reallocation id for function */
|
||||||
|
int Library::getReallocId(const Token *tok, int arg) const
|
||||||
|
{
|
||||||
|
const Library::AllocFunc* af = getReallocFuncInfo(tok);
|
||||||
return (af && af->arg == arg) ? af->groupId : 0;
|
return (af && af->arg == arg) ? af->groupId : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,27 +77,34 @@ public:
|
||||||
BufferSize bufferSize;
|
BufferSize bufferSize;
|
||||||
int bufferSizeArg1;
|
int bufferSizeArg1;
|
||||||
int bufferSizeArg2;
|
int bufferSizeArg2;
|
||||||
|
int reallocArg;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** get allocation info for function */
|
/** get allocation info for function */
|
||||||
const AllocFunc* alloc(const Token *tok) const;
|
const AllocFunc* getAllocFuncInfo(const Token *tok) const;
|
||||||
|
|
||||||
/** get deallocation info for function */
|
/** get deallocation info for function */
|
||||||
const AllocFunc* dealloc(const Token *tok) const;
|
const AllocFunc* getDeallocFuncInfo(const Token *tok) const;
|
||||||
|
|
||||||
|
/** get reallocation info for function */
|
||||||
|
const AllocFunc* getReallocFuncInfo(const Token *tok) const;
|
||||||
|
|
||||||
/** get allocation id for function */
|
/** get allocation id for function */
|
||||||
int alloc(const Token *tok, int arg) const;
|
int getAllocId(const Token *tok, int arg) const;
|
||||||
|
|
||||||
/** get deallocation id for function */
|
/** get deallocation id for function */
|
||||||
int dealloc(const Token *tok, int arg) const;
|
int getDeallocId(const Token *tok, int arg) const;
|
||||||
|
|
||||||
|
/** get reallocation id for function */
|
||||||
|
int getReallocId(const Token *tok, int arg) const;
|
||||||
|
|
||||||
/** get allocation info for function by name (deprecated, use other alloc) */
|
/** get allocation info for function by name (deprecated, use other alloc) */
|
||||||
const AllocFunc* alloc(const char name[]) const {
|
const AllocFunc* getAllocFuncInfo(const char name[]) const {
|
||||||
return getAllocDealloc(mAlloc, name);
|
return getAllocDealloc(mAlloc, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get deallocation info for function by name (deprecated, use other alloc) */
|
/** get deallocation info for function by name (deprecated, use other alloc) */
|
||||||
const AllocFunc* dealloc(const char name[]) const {
|
const AllocFunc* getDeallocFuncInfo(const char name[]) const {
|
||||||
return getAllocDealloc(mDealloc, name);
|
return getAllocDealloc(mDealloc, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +131,12 @@ public:
|
||||||
mDealloc[functionname].arg = arg;
|
mDealloc[functionname].arg = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setrealloc(const std::string &functionname, int id, int arg, int reallocArg = 1) {
|
||||||
|
mRealloc[functionname].groupId = id;
|
||||||
|
mRealloc[functionname].arg = arg;
|
||||||
|
mRealloc[functionname].reallocArg = reallocArg;
|
||||||
|
}
|
||||||
|
|
||||||
/** add noreturn function setting */
|
/** add noreturn function setting */
|
||||||
void setnoreturn(const std::string& funcname, bool noreturn) {
|
void setnoreturn(const std::string& funcname, bool noreturn) {
|
||||||
mNoReturn[funcname] = noreturn;
|
mNoReturn[funcname] = noreturn;
|
||||||
|
@ -523,6 +536,7 @@ private:
|
||||||
std::set<std::string> mFiles;
|
std::set<std::string> mFiles;
|
||||||
std::map<std::string, AllocFunc> mAlloc; // allocation functions
|
std::map<std::string, AllocFunc> mAlloc; // allocation functions
|
||||||
std::map<std::string, AllocFunc> mDealloc; // deallocation functions
|
std::map<std::string, AllocFunc> mDealloc; // deallocation functions
|
||||||
|
std::map<std::string, AllocFunc> mRealloc; // reallocation functions
|
||||||
std::map<std::string, bool> mNoReturn; // is function noreturn?
|
std::map<std::string, bool> mNoReturn; // is function noreturn?
|
||||||
std::map<std::string, std::string> mReturnValue;
|
std::map<std::string, std::string> mReturnValue;
|
||||||
std::map<std::string, std::string> mReturnValueType;
|
std::map<std::string, std::string> mReturnValueType;
|
||||||
|
|
|
@ -5310,7 +5310,9 @@ static void valueFlowDynamicBufferSize(TokenList *tokenlist, SymbolDatabase *sym
|
||||||
if (!Token::Match(rhs->previous(), "%name% ("))
|
if (!Token::Match(rhs->previous(), "%name% ("))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Library::AllocFunc *allocFunc = settings->library.alloc(rhs->previous());
|
const Library::AllocFunc *allocFunc = settings->library.getAllocFuncInfo(rhs->previous());
|
||||||
|
if (!allocFunc)
|
||||||
|
allocFunc = settings->library.getReallocFuncInfo(rhs->previous());
|
||||||
if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none)
|
if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -1183,7 +1183,7 @@ Checking test.c...
|
||||||
functions do not affect the allocation at all.</para>
|
functions do not affect the allocation at all.</para>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>alloc and dealloc</title>
|
<title>alloc, realloc and dealloc</title>
|
||||||
|
|
||||||
<para>Here is an example program:</para>
|
<para>Here is an example program:</para>
|
||||||
|
|
||||||
|
@ -1217,6 +1217,17 @@ Checking pen1.c...
|
||||||
</resource>
|
</resource>
|
||||||
</def></programlisting>
|
</def></programlisting>
|
||||||
|
|
||||||
|
<para>Functions that reallocate memory can be configured using a realloc tag. The input argument which points to the memory that shall be reallocated can also be configured (the default is the first argument). As an example, here is a configuration file for the fopen, freopen and fclose functions from the c standard library:
|
||||||
|
|
||||||
|
<programlisting><?xml version="1.0"?>
|
||||||
|
<def>
|
||||||
|
<resource>
|
||||||
|
<alloc>fopen</alloc>
|
||||||
|
<realloc realloc-arg="3">freopen</realloc>
|
||||||
|
<dealloc>fclose</dealloc>
|
||||||
|
</resource>
|
||||||
|
</def></programlisting>
|
||||||
|
|
||||||
<para>The allocation and deallocation functions are organized in
|
<para>The allocation and deallocation functions are organized in
|
||||||
groups. Each group is defined in a <literal><resource></literal>
|
groups. Each group is defined in a <literal><resource></literal>
|
||||||
or <literal><memory></literal> tag and is identified by its
|
or <literal><memory></literal> tag and is identified by its
|
||||||
|
|
|
@ -15,7 +15,7 @@ This is a reference for the .cfg file format that Cppcheck uses.
|
||||||
|
|
||||||
Cppcheck has configurable checking for leaks, e.g. you can specify which functions allocate and free memory or resources and which functions do not affect the allocation at all.
|
Cppcheck has configurable checking for leaks, e.g. you can specify which functions allocate and free memory or resources and which functions do not affect the allocation at all.
|
||||||
|
|
||||||
## `<alloc>` and `<dealloc>`
|
## `<alloc>`, `<realloc>` and `<dealloc>`
|
||||||
|
|
||||||
Here is an example program:
|
Here is an example program:
|
||||||
|
|
||||||
|
@ -45,6 +45,17 @@ Here is a minimal windows.cfg file:
|
||||||
</resource>
|
</resource>
|
||||||
</def>
|
</def>
|
||||||
|
|
||||||
|
Functions that reallocate memory can be configured using a `<realloc>` tag. The input argument which points to the memory that shall be reallocated can also be configured (the default is the first argument). As an example, here is a configuration file for the fopen, freopen and fclose functions from the c standard library:
|
||||||
|
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<def>
|
||||||
|
<resource>
|
||||||
|
<alloc>fopen</alloc>
|
||||||
|
<realloc realloc-arg="3">freopen</realloc>
|
||||||
|
<dealloc>fclose</dealloc>
|
||||||
|
</resource>
|
||||||
|
</def>
|
||||||
|
|
||||||
The allocation and deallocation functions are organized in groups. Each group is defined in a `<resource>` or `<memory>` tag and is identified by its `<dealloc>` functions. This means, groups with overlapping `<dealloc>` tags are merged.
|
The allocation and deallocation functions are organized in groups. Each group is defined in a `<resource>` or `<memory>` tag and is identified by its `<dealloc>` functions. This means, groups with overlapping `<dealloc>` tags are merged.
|
||||||
|
|
||||||
## `<leak-ignore>` and `<use>`
|
## `<leak-ignore>` and `<use>`
|
||||||
|
|
|
@ -1434,7 +1434,7 @@ void uninitvar_freopen(void)
|
||||||
FILE *stream;
|
FILE *stream;
|
||||||
// cppcheck-suppress uninitvar
|
// cppcheck-suppress uninitvar
|
||||||
FILE * p = freopen(filename,mode,stream);
|
FILE * p = freopen(filename,mode,stream);
|
||||||
free(p);
|
fclose(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninitvar_frexp(void)
|
void uninitvar_frexp(void)
|
||||||
|
|
|
@ -36,9 +36,11 @@ private:
|
||||||
int id = 0;
|
int id = 0;
|
||||||
while (!settings.library.ismemory(++id));
|
while (!settings.library.ismemory(++id));
|
||||||
settings.library.setalloc("malloc", id, -1);
|
settings.library.setalloc("malloc", id, -1);
|
||||||
|
settings.library.setrealloc("realloc", id, -1);
|
||||||
settings.library.setdealloc("free", id, 1);
|
settings.library.setdealloc("free", id, 1);
|
||||||
while (!settings.library.isresource(++id));
|
while (!settings.library.isresource(++id));
|
||||||
settings.library.setalloc("fopen", id, -1);
|
settings.library.setalloc("fopen", id, -1);
|
||||||
|
settings.library.setrealloc("freopen", id, -1, 3);
|
||||||
settings.library.setdealloc("fclose", id, 1);
|
settings.library.setdealloc("fclose", id, 1);
|
||||||
settings.library.smartPointers.insert("std::shared_ptr");
|
settings.library.smartPointers.insert("std::shared_ptr");
|
||||||
settings.library.smartPointers.insert("std::unique_ptr");
|
settings.library.smartPointers.insert("std::unique_ptr");
|
||||||
|
@ -63,6 +65,12 @@ private:
|
||||||
TEST_CASE(assign17); // #9047
|
TEST_CASE(assign17); // #9047
|
||||||
TEST_CASE(assign18);
|
TEST_CASE(assign18);
|
||||||
|
|
||||||
|
TEST_CASE(realloc1);
|
||||||
|
TEST_CASE(realloc2);
|
||||||
|
TEST_CASE(realloc3);
|
||||||
|
TEST_CASE(freopen1);
|
||||||
|
TEST_CASE(freopen2);
|
||||||
|
|
||||||
TEST_CASE(deallocuse1);
|
TEST_CASE(deallocuse1);
|
||||||
TEST_CASE(deallocuse2);
|
TEST_CASE(deallocuse2);
|
||||||
TEST_CASE(deallocuse3);
|
TEST_CASE(deallocuse3);
|
||||||
|
@ -350,6 +358,48 @@ private:
|
||||||
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void realloc1() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" void *p = malloc(10);\n"
|
||||||
|
" void *q = realloc(p, 20);\n"
|
||||||
|
" free(q)\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc2() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" void *p = malloc(10);\n"
|
||||||
|
" void *q = realloc(p, 20);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc3() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" char *p = malloc(10);\n"
|
||||||
|
" char *q = (char*) realloc(p, 20);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void freopen1() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" void *p = fopen(name,a);\n"
|
||||||
|
" void *q = freopen(name, b, p);\n"
|
||||||
|
" fclose(q)\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void freopen2() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" void *p = fopen(name,a);\n"
|
||||||
|
" void *q = freopen(name, b, p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:4]: (error) Resource leak: q\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void deallocuse1() {
|
void deallocuse1() {
|
||||||
check("void f(char *p) {\n"
|
check("void f(char *p) {\n"
|
||||||
" free(p);\n"
|
" free(p);\n"
|
||||||
|
@ -1327,6 +1377,29 @@ private:
|
||||||
" std::unique_ptr<int[]> x(i);\n"
|
" std::unique_ptr<int[]> x(i);\n"
|
||||||
"}\n", true);
|
"}\n", true);
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" void* a = malloc(1);\n"
|
||||||
|
" void* b = freopen(f, p, a);\n"
|
||||||
|
" free(b);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.c:3]: (error) Mismatching allocation and deallocation: a\n"
|
||||||
|
"[test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" void* a;\n"
|
||||||
|
" void* b = realloc(a, 10);\n"
|
||||||
|
" free(b);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int * i = new int;\n"
|
||||||
|
" int * j = realloc(i, 2 * sizeof(int));\n"
|
||||||
|
" delete[] j;\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching allocation and deallocation: i\n"
|
||||||
|
"[test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void smartPointerDeleter() {
|
void smartPointerDeleter() {
|
||||||
|
|
|
@ -622,11 +622,11 @@ private:
|
||||||
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
||||||
ASSERT(library.functions.empty());
|
ASSERT(library.functions.empty());
|
||||||
|
|
||||||
ASSERT(Library::ismemory(library.alloc("CreateX")));
|
ASSERT(Library::ismemory(library.getAllocFuncInfo("CreateX")));
|
||||||
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
|
ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX"));
|
||||||
const Library::AllocFunc* af = library.alloc("CreateX");
|
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
|
||||||
ASSERT(af && af->arg == -1);
|
ASSERT(af && af->arg == -1);
|
||||||
const Library::AllocFunc* df = library.dealloc("DeleteX");
|
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
|
||||||
ASSERT(df && df->arg == 1);
|
ASSERT(df && df->arg == 1);
|
||||||
}
|
}
|
||||||
void memory2() const {
|
void memory2() const {
|
||||||
|
@ -665,9 +665,9 @@ private:
|
||||||
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
||||||
ASSERT(library.functions.empty());
|
ASSERT(library.functions.empty());
|
||||||
|
|
||||||
const Library::AllocFunc* af = library.alloc("CreateX");
|
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
|
||||||
ASSERT(af && af->arg == 5);
|
ASSERT(af && af->arg == 5);
|
||||||
const Library::AllocFunc* df = library.dealloc("DeleteX");
|
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
|
||||||
ASSERT(df && df->arg == 2);
|
ASSERT(df && df->arg == 2);
|
||||||
|
|
||||||
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
|
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
|
||||||
|
|
|
@ -121,6 +121,235 @@ REGISTER_TEST(TestMemleak)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestMemleakInFunction : public TestFixture {
|
||||||
|
public:
|
||||||
|
TestMemleakInFunction() : TestFixture("TestMemleakInFunction") {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Settings settings0;
|
||||||
|
Settings settings1;
|
||||||
|
Settings settings2;
|
||||||
|
|
||||||
|
void check(const char code[]) {
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
Settings *settings = &settings1;
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
tokenizer.simplifyTokenList2();
|
||||||
|
|
||||||
|
// Check for memory leaks..
|
||||||
|
CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this);
|
||||||
|
checkMemoryLeak.checkReallocUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void run() OVERRIDE {
|
||||||
|
LOAD_LIB_2(settings1.library, "std.cfg");
|
||||||
|
LOAD_LIB_2(settings1.library, "posix.cfg");
|
||||||
|
LOAD_LIB_2(settings2.library, "std.cfg");
|
||||||
|
|
||||||
|
TEST_CASE(realloc1);
|
||||||
|
TEST_CASE(realloc2);
|
||||||
|
TEST_CASE(realloc3);
|
||||||
|
TEST_CASE(realloc4);
|
||||||
|
TEST_CASE(realloc5);
|
||||||
|
TEST_CASE(realloc7);
|
||||||
|
TEST_CASE(realloc8);
|
||||||
|
TEST_CASE(realloc9);
|
||||||
|
TEST_CASE(realloc10);
|
||||||
|
TEST_CASE(realloc11);
|
||||||
|
TEST_CASE(realloc12);
|
||||||
|
TEST_CASE(realloc13);
|
||||||
|
TEST_CASE(realloc14);
|
||||||
|
TEST_CASE(realloc15);
|
||||||
|
TEST_CASE(realloc16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc1() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *a = (char *)malloc(10);\n"
|
||||||
|
" a = realloc(a, 100);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc2() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *a = (char *)malloc(10);\n"
|
||||||
|
" a = (char *)realloc(a, 100);\n"
|
||||||
|
" free(a);\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc3() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *a = 0;\n"
|
||||||
|
" if ((a = realloc(a, 100)) == NULL)\n"
|
||||||
|
" return;\n"
|
||||||
|
" free(a);\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc4() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" static char *a = 0;\n"
|
||||||
|
" if ((a = realloc(a, 100)) == NULL)\n"
|
||||||
|
" return;\n"
|
||||||
|
" free(a);\n"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n",
|
||||||
|
"[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n",
|
||||||
|
errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc5() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *buf;\n"
|
||||||
|
" char *new_buf;\n"
|
||||||
|
" buf = calloc( 10 );\n"
|
||||||
|
" new_buf = realloc ( buf, 20);\n"
|
||||||
|
" if ( !new_buf )\n"
|
||||||
|
" free(buf);\n"
|
||||||
|
" else\n"
|
||||||
|
" free(new_buf);\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc7() {
|
||||||
|
check("bool foo(size_t nLen, char* pData)\n"
|
||||||
|
"{\n"
|
||||||
|
" pData = (char*) realloc(pData, sizeof(char) + (nLen + 1)*sizeof(char));\n"
|
||||||
|
" if ( pData == NULL )\n"
|
||||||
|
" {\n"
|
||||||
|
" return false;\n"
|
||||||
|
" }\n"
|
||||||
|
" free(pData);\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc8() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char *origBuf = m_buf;\n"
|
||||||
|
" m_buf = (char *) realloc (m_buf, m_capacity + growBy);\n"
|
||||||
|
" if (!m_buf) {\n"
|
||||||
|
" m_buf = origBuf;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc9() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" x = realloc(x,100);\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc10() {
|
||||||
|
check("void foo() {\n"
|
||||||
|
" char *pa, *pb;\n"
|
||||||
|
" pa = pb = malloc(10);\n"
|
||||||
|
" pa = realloc(pa, 20);"
|
||||||
|
" exit();\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc11() {
|
||||||
|
check("void foo() {\n"
|
||||||
|
" char *p;\n"
|
||||||
|
" p = realloc(p, size);\n"
|
||||||
|
" if (!p)\n"
|
||||||
|
" error();\n"
|
||||||
|
" usep(p);\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc12() {
|
||||||
|
check("void foo(int x)\n"
|
||||||
|
"{\n"
|
||||||
|
" char *a = 0;\n"
|
||||||
|
" if ((a = realloc(a, x + 100)) == NULL)\n"
|
||||||
|
" return;\n"
|
||||||
|
" free(a);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc13() {
|
||||||
|
check("void foo()\n"
|
||||||
|
"{\n"
|
||||||
|
" char **str;\n"
|
||||||
|
" *str = realloc(*str,100);\n"
|
||||||
|
" free (*str);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'str\' nulled but not freed upon failure\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc14() {
|
||||||
|
check("void foo() {\n"
|
||||||
|
" char *p;\n"
|
||||||
|
" p = realloc(p, size + 1);\n"
|
||||||
|
" if (!p)\n"
|
||||||
|
" error();\n"
|
||||||
|
" usep(p);\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc15() {
|
||||||
|
check("bool foo() {\n"
|
||||||
|
" char ** m_options;\n"
|
||||||
|
" m_options = (char**)realloc( m_options, 2 * sizeof(char*));\n"
|
||||||
|
" if( m_options == NULL )\n"
|
||||||
|
" return false;\n"
|
||||||
|
" return true;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void realloc16() {
|
||||||
|
check("void f(char *zLine) {\n"
|
||||||
|
" zLine = realloc(zLine, 42);\n"
|
||||||
|
" if (zLine) {\n"
|
||||||
|
" free(zLine);\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_TEST(TestMemleakInFunction)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestMemleakInClass : public TestFixture {
|
class TestMemleakInClass : public TestFixture {
|
||||||
public:
|
public:
|
||||||
TestMemleakInClass() : TestFixture("TestMemleakInClass") {
|
TestMemleakInClass() : TestFixture("TestMemleakInClass") {
|
||||||
|
|
|
@ -3887,6 +3887,20 @@ private:
|
||||||
" return x;\n"
|
" return x;\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 4U, 5, ValueFlow::Value::BUFFER_SIZE));
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 5, ValueFlow::Value::BUFFER_SIZE));
|
||||||
|
|
||||||
|
code = "void* f() {\n"
|
||||||
|
" void* y = malloc(10);\n"
|
||||||
|
" void* x = realloc(y, 20);\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 20, ValueFlow::Value::BUFFER_SIZE));
|
||||||
|
|
||||||
|
code = "void* f() {\n"
|
||||||
|
" void* y = calloc(10, 4);\n"
|
||||||
|
" void* x = reallocarray(y, 20, 5);\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::BUFFER_SIZE));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue