* 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 9765a2dfab4f59102507fe759ef2184b5ed41c9f. 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>
|
||||
<ref name="DATA-NAME"/>
|
||||
</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>
|
||||
</choice>
|
||||
</zeroOrMore>
|
||||
@ -62,6 +77,18 @@
|
||||
</optional>
|
||||
<ref name="DATA-NAME"/>
|
||||
</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>
|
||||
</choice>
|
||||
</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="true" buffer-size="calloc">calloc</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>
|
||||
</memory>
|
||||
<resource>
|
||||
<alloc init="true">fopen</alloc>
|
||||
<alloc init="true">tmpfile</alloc>
|
||||
<realloc init="true" realloc-arg="3">freopen</realloc>
|
||||
<dealloc>fclose</dealloc>
|
||||
</resource>
|
||||
<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);
|
||||
}
|
||||
// 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))
|
||||
|
@ -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;
|
||||
|
@ -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<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)
|
||||
{
|
||||
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?
|
||||
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;
|
||||
}
|
||||
|
@ -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<unsigned int, VarInfo::AllocInfo> &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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<std::string> mFiles;
|
||||
std::map<std::string, AllocFunc> mAlloc; // allocation 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, std::string> mReturnValue;
|
||||
std::map<std::string, std::string> mReturnValueType;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1183,7 +1183,7 @@ Checking test.c...
|
||||
functions do not affect the allocation at all.</para>
|
||||
|
||||
<section>
|
||||
<title>alloc and dealloc</title>
|
||||
<title>alloc, realloc and dealloc</title>
|
||||
|
||||
<para>Here is an example program:</para>
|
||||
|
||||
@ -1217,6 +1217,17 @@ Checking pen1.c...
|
||||
</resource>
|
||||
</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
|
||||
groups. Each group is defined in a <literal><resource></literal>
|
||||
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.
|
||||
|
||||
## `<alloc>` and `<dealloc>`
|
||||
## `<alloc>`, `<realloc>` and `<dealloc>`
|
||||
|
||||
Here is an example program:
|
||||
|
||||
@ -45,6 +45,17 @@ Here is a minimal windows.cfg file:
|
||||
</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 `<dealloc>` functions. This means, groups with overlapping `<dealloc>` tags are merged.
|
||||
|
||||
## `<leak-ignore>` and `<use>`
|
||||
|
@ -1434,7 +1434,7 @@ void uninitvar_freopen(void)
|
||||
FILE *stream;
|
||||
// cppcheck-suppress uninitvar
|
||||
FILE * p = freopen(filename,mode,stream);
|
||||
free(p);
|
||||
fclose(p);
|
||||
}
|
||||
|
||||
void uninitvar_frexp(void)
|
||||
|
@ -36,9 +36,11 @@ private:
|
||||
int id = 0;
|
||||
while (!settings.library.ismemory(++id));
|
||||
settings.library.setalloc("malloc", id, -1);
|
||||
settings.library.setrealloc("realloc", id, -1);
|
||||
settings.library.setdealloc("free", id, 1);
|
||||
while (!settings.library.isresource(++id));
|
||||
settings.library.setalloc("fopen", id, -1);
|
||||
settings.library.setrealloc("freopen", id, -1, 3);
|
||||
settings.library.setdealloc("fclose", id, 1);
|
||||
settings.library.smartPointers.insert("std::shared_ptr");
|
||||
settings.library.smartPointers.insert("std::unique_ptr");
|
||||
@ -63,6 +65,12 @@ private:
|
||||
TEST_CASE(assign17); // #9047
|
||||
TEST_CASE(assign18);
|
||||
|
||||
TEST_CASE(realloc1);
|
||||
TEST_CASE(realloc2);
|
||||
TEST_CASE(realloc3);
|
||||
TEST_CASE(freopen1);
|
||||
TEST_CASE(freopen2);
|
||||
|
||||
TEST_CASE(deallocuse1);
|
||||
TEST_CASE(deallocuse2);
|
||||
TEST_CASE(deallocuse3);
|
||||
@ -350,6 +358,48 @@ private:
|
||||
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() {
|
||||
check("void f(char *p) {\n"
|
||||
" free(p);\n"
|
||||
@ -1327,6 +1377,29 @@ private:
|
||||
" std::unique_ptr<int[]> x(i);\n"
|
||||
"}\n", true);
|
||||
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() {
|
||||
|
@ -622,11 +622,11 @@ private:
|
||||
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
||||
ASSERT(library.functions.empty());
|
||||
|
||||
ASSERT(Library::ismemory(library.alloc("CreateX")));
|
||||
ASSERT(Library::ismemory(library.getAllocFuncInfo("CreateX")));
|
||||
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);
|
||||
const Library::AllocFunc* df = library.dealloc("DeleteX");
|
||||
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
|
||||
ASSERT(df && df->arg == 1);
|
||||
}
|
||||
void memory2() const {
|
||||
@ -665,9 +665,9 @@ private:
|
||||
ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode);
|
||||
ASSERT(library.functions.empty());
|
||||
|
||||
const Library::AllocFunc* af = library.alloc("CreateX");
|
||||
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
|
||||
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(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 {
|
||||
public:
|
||||
TestMemleakInClass() : TestFixture("TestMemleakInClass") {
|
||||
|
@ -3887,6 +3887,20 @@ private:
|
||||
" return x;\n"
|
||||
"}";
|
||||
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…
x
Reference in New Issue
Block a user