diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng
index 2a52b3203..6aaeaba8d 100644
--- a/cfg/cppcheck-cfg.rng
+++ b/cfg/cppcheck-cfg.rng
@@ -37,6 +37,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -62,6 +77,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cfg/std.cfg b/cfg/std.cfg
index 83f7b6036..84079a959 100644
--- a/cfg/std.cfg
+++ b/cfg/std.cfg
@@ -7506,11 +7506,14 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
malloc
calloc
aligned_alloc
+ realloc
+ reallocarray
free
fopen
tmpfile
+ freopen
fclose
diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp
index 3f7be8672..9c76190df 100644
--- a/lib/checkautovariables.cpp
+++ b/lib/checkautovariables.cpp
@@ -296,7 +296,7 @@ void CheckAutoVariables::autoVariables()
errorAutoVariableAssignment(tok->next(), false);
}
// 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% !!["))) {
tok = Token::findmatch(tok->next(), "%var%");
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% !!["))) {
tok = Token::findmatch(tok->next(), "%var%");
if (isAutoVar(tok))
diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp
index 5cf2da01a..1548d5034 100644
--- a/lib/checkclass.cpp
+++ b/lib/checkclass.cpp
@@ -331,7 +331,7 @@ void CheckClass::copyconstructors()
const Token* tok = func.token->linkAt(1);
for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) {
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();
if (var && var->isPointer() && var->scope() == scope)
allocatedVars[tok->varId()] = tok;
@@ -339,7 +339,7 @@ void CheckClass::copyconstructors()
}
for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) {
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();
if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
allocatedVars[tok->varId()] = tok;
diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp
index aecd93fe8..b2553173a 100644
--- a/lib/checkleakautovar.cpp
+++ b/lib/checkleakautovar.cpp
@@ -335,13 +335,16 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
}
// allocation?
- if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
- const Library::AllocFunc* f = mSettings->library.alloc(tokRightAstOperand->previous());
+ const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr;
+ if (Token::Match(fTok, "%type% (")) {
+ const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok);
if (f && f->arg == -1) {
VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
varAlloc.type = f->groupId;
varAlloc.status = VarInfo::ALLOC;
}
+
+ changeAllocStatusIfRealloc(alloctype, fTok, varTok);
} else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) {
const Token* tok2 = varTok->tokAt(2)->astOperand1();
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())
tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
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) {
VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
varAlloc.type = f->groupId;
@@ -384,6 +387,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
// Fixme: warn about leak
alloctype.erase(innerTok->varId());
}
+
+ changeAllocStatusIfRealloc(alloctype, innerTok->tokAt(2), varTok);
} else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) {
const Token* tok2 = innerTok->tokAt(2)->astOperand1();
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..
else if (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);
if (allocation.type == 0)
allocation.status = VarInfo::NOALLOC;
@@ -636,7 +641,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
// Check if its a pointer to a function
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
if (dtok) {
- af = mSettings->library.dealloc(dtok->tokAt(1));
+ af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
} else {
const Token * tscopeStart = nullptr;
const Token * tscopeEnd = nullptr;
@@ -658,7 +663,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
if (tscopeStart && tscopeEnd) {
for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) {
- af = mSettings->library.dealloc(tok2);
+ af = mSettings->library.getDeallocFuncInfo(tok2);
if (af)
break;
}
@@ -697,7 +702,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
// check for function call
const Token * const openingPar = isFunctionCall(tok);
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);
if (alloc.type == 0)
alloc.status = VarInfo::NOALLOC;
@@ -709,6 +714,22 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t
}
+void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &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)
{
std::map &alloctype = varInfo->alloctype;
@@ -740,6 +761,8 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
// Ignore function call?
if (mSettings->library.isLeakIgnore(tokName->str()))
return;
+ if (mSettings->library.getReallocFuncInfo(tokName))
+ return;
const Token * const tokFirstArg = tokOpeningPar->next();
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
const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
if (dtok) {
- sp_af = mSettings->library.dealloc(dtok->tokAt(1));
+ sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
} else {
// If the deleter is a class, check if class calls the dealloc function
dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken);
if (dtok && dtok->type()) {
const Scope * tscope = dtok->type()->classScope;
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)
break;
}
diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h
index 2b4ab49dd..a7acc2ee4 100644
--- a/lib/checkleakautovar.h
+++ b/lib/checkleakautovar.h
@@ -131,6 +131,9 @@ private:
/** parse changes in allocation status */
void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg);
+ /** update allocation status if reallocation function */
+ void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok);
+
/** return. either "return" or end of variable scope is seen */
void ret(const Token *tok, const VarInfo &varInfo);
diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp
index 06324799a..23c6a1a8e 100644
--- a/lib/checkmemoryleak.cpp
+++ b/lib/checkmemoryleak.cpp
@@ -172,7 +172,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2,
}
// 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 == mSettings_->library.deallocId("free"))
return Malloc;
@@ -263,7 +263,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok
}
// 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 == mSettings_->library.deallocId("free"))
return Malloc;
diff --git a/lib/library.cpp b/lib/library.cpp
index affcb26cf..393088179 100644
--- a/lib/library.cpp
+++ b/lib/library.cpp
@@ -188,7 +188,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
// add alloc/dealloc/use functions..
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
const std::string memorynodename = memorynode->Name();
- if (memorynodename == "alloc") {
+ if (memorynodename == "alloc" || memorynodename == "realloc") {
AllocFunc temp = {0};
temp.groupId = allocationId;
@@ -225,7 +225,18 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
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") {
AllocFunc temp = {0};
temp.groupId = allocationId;
@@ -934,30 +945,44 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const
/** 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);
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname);
}
/** 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);
return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname);
}
-/** get allocation id for function */
-int Library::alloc(const Token *tok, int arg) const
+/** get reallocation info for function */
+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;
}
/** 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;
}
diff --git a/lib/library.h b/lib/library.h
index d550b139a..2dc4449fd 100644
--- a/lib/library.h
+++ b/lib/library.h
@@ -77,27 +77,34 @@ public:
BufferSize bufferSize;
int bufferSizeArg1;
int bufferSizeArg2;
+ int reallocArg;
};
/** get allocation info for function */
- const AllocFunc* alloc(const Token *tok) const;
+ const AllocFunc* getAllocFuncInfo(const Token *tok) const;
/** 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 */
- int alloc(const Token *tok, int arg) const;
+ int getAllocId(const Token *tok, int arg) const;
/** 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) */
- const AllocFunc* alloc(const char name[]) const {
+ const AllocFunc* getAllocFuncInfo(const char name[]) const {
return getAllocDealloc(mAlloc, name);
}
/** 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);
}
@@ -124,6 +131,12 @@ public:
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 */
void setnoreturn(const std::string& funcname, bool noreturn) {
mNoReturn[funcname] = noreturn;
@@ -523,6 +536,7 @@ private:
std::set mFiles;
std::map mAlloc; // allocation functions
std::map mDealloc; // deallocation functions
+ std::map mRealloc; // reallocation functions
std::map mNoReturn; // is function noreturn?
std::map mReturnValue;
std::map mReturnValueType;
diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp
index f90e88922..0ff9f6abf 100644
--- a/lib/valueflow.cpp
+++ b/lib/valueflow.cpp
@@ -5310,7 +5310,9 @@ static void valueFlowDynamicBufferSize(TokenList *tokenlist, SymbolDatabase *sym
if (!Token::Match(rhs->previous(), "%name% ("))
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)
continue;
diff --git a/man/manual.docbook b/man/manual.docbook
index 519773ed8..a72bed604 100644
--- a/man/manual.docbook
+++ b/man/manual.docbook
@@ -1183,7 +1183,7 @@ Checking test.c...
functions do not affect the allocation at all.
- alloc and dealloc
+ alloc, realloc and dealloc
Here is an example program:
@@ -1217,6 +1217,17 @@ Checking pen1.c...
</resource>
</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
diff --git a/man/reference-cfg-format.md b/man/reference-cfg-format.md
index c736cff2c..f15b471b6 100644
--- a/man/reference-cfg-format.md
+++ b/man/reference-cfg-format.md
@@ -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.
-## `` and ``
+## ``, `` and ``
Here is an example program:
@@ -45,6 +45,17 @@ Here is a minimal windows.cfg file:
+Functions that reallocate memory can be configured using a `` 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:
+
+
+
+
+ fopen
+ freopen
+ fclose
+
+
+
The allocation and deallocation functions are organized in groups. Each group is defined in a `` or `` tag and is identified by its `` functions. This means, groups with overlapping `` tags are merged.
## `` and `