Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

480 lines (387 sloc) 18.053 kb
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2015 Daniel Marjamäki and Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#ifndef checkmemoryleakH
#define checkmemoryleakH
//---------------------------------------------------------------------------
/**
* @file
*
* %Check for memory leaks
*
* The checking is split up into three specialized classes.
* - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly.
* - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly.
* - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members
*/
#include "config.h"
#include "check.h"
#include <list>
#include <string>
class Scope;
class Function;
class Variable;
/// @addtogroup Core
/// @{
/** @brief Base class for memory leaks checking */
class CPPCHECKLIB CheckMemoryLeak {
protected:
/** For access to the tokens */
const Tokenizer * const tokenizer;
private:
/** ErrorLogger used to report errors */
ErrorLogger * const errorLogger;
/** Enabled standards */
const Settings * const settings1;
/** Disable the default constructors */
CheckMemoryLeak();
/** Disable the default constructors */
CheckMemoryLeak(const CheckMemoryLeak &);
/** disable assignment operator */
void operator=(const CheckMemoryLeak &);
/**
* Report error. Similar with the function Check::reportError
* @param location the token where the error occurs
* @param severity the severity of the bug
* @param id type of message
* @param msg text
* @param cwe cwe number
*/
void reportErr(const Token *location, Severity::SeverityType severity, const std::string &id, const std::string &msg, unsigned int cwe) const;
/**
* Report error. Similar with the function Check::reportError
* @param callstack callstack of error
* @param severity the severity of the bug
* @param id type of message
* @param msg text
* @param cwe cwe number
*/
void reportErr(const std::list<const Token *> &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, unsigned int cwe) const;
public:
CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s)
: tokenizer(t), errorLogger(e), settings1(s) {
}
/** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many };
void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype);
/**
* @brief Get type of deallocation at given position
* @param tok position
* @param varname variable name
* @return type of deallocation
*/
AllocType getDeallocationType(const Token *tok, const std::string &varname) const;
/**
* @brief Get type of deallocation at given position
* @param tok position
* @param varid variable id
* @return type of deallocation
*/
AllocType getDeallocationType(const Token *tok, unsigned int varid) const;
/**
* @brief Get type of allocation at given position
*/
AllocType getAllocationType(const Token *tok2, unsigned int varid, std::list<const Function*> *callstack = NULL) const;
/**
* @brief Get type of reallocation at given position
*/
static AllocType getReallocationType(const Token *tok2, unsigned int varid);
/**
* @brief Is a typename the name of a class?
* @param tok type token
* @param varid variable id
* @return true if the type name is the name of a class
*/
bool isclass(const Token *tok, unsigned int varid) const;
/**
* Report that there is a memory leak (new/malloc/etc)
* @param tok token where memory is leaked
* @param varname name of variable
*/
void memleakError(const Token *tok, const std::string &varname) const;
/**
* Report that there is a resource leak (fopen/popen/etc)
* @param tok token where resource is leaked
* @param varname name of variable
*/
void resourceLeakError(const Token *tok, const std::string &varname) const;
/**
* @brief Report error: deallocating a deallocated pointer
* @param tok token where error occurs
* @param varname name of variable
*/
void deallocDeallocError(const Token *tok, const std::string &varname) const;
void deallocuseError(const Token *tok, const std::string &varname) const;
void mismatchSizeError(const Token *tok, const std::string &sz) const;
void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname) const;
void memleakUponReallocFailureError(const Token *tok, const std::string &varname) const;
/** What type of allocated memory does the given function return? */
AllocType functionReturnType(const Function* func, std::list<const Function*> *callstack = NULL) const;
/** Function allocates pointed-to argument (a la asprintf)? */
const char *functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const;
};
/// @}
/// @addtogroup Checks
/// @{
/**
* @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly.
*
* The checking is done by looking at each function variable separately. By repeating these 4 steps over and over:
* -# locate a function variable
* -# create a simple token list that describes the usage of the function variable.
* -# simplify the token list.
* -# finally, check if the simplified token list contain any leaks.
*/
class CPPCHECKLIB CheckMemoryLeakInFunction : private Check, public CheckMemoryLeak {
public:
/** @brief This constructor is used when registering this class */
CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(0, 0, 0), symbolDatabase(NULL) {
}
/** @brief This constructor is used when running checks */
CheckMemoryLeakInFunction(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
: Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
// get the symbol database
if (tokenizr)
symbolDatabase = tokenizr->getSymbolDatabase();
else
symbolDatabase = 0;
}
/** @brief run all simplified checks */
void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
CheckMemoryLeakInFunction checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.checkReallocUsage();
checkMemoryLeak.check();
}
/** @brief Unit testing : testing the white list */
static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp);
/** @brief Perform checking */
void check();
/**
* Checking for a memory leak caused by improper realloc usage.
*/
void checkReallocUsage();
/**
* @brief %Check if there is a "!var" match inside a condition
* @param tok first token to match
* @param varid variable id
* @param endpar if this is true the "!var" must be followed by ")"
* @return true if match
*/
static bool notvar(const Token *tok, unsigned int varid, bool endpar = false);
/**
* Inspect a function call. the call_func and getcode are recursive
* @param tok token where the function call occurs
* @param callstack callstack
* @param varid variable id to check
* @param alloctype if memory is allocated, this indicates the type of allocation
* @param dealloctype if memory is deallocated, this indicates the type of deallocation
* @param allocpar if function allocates varid parameter
* @param sz not used by call_func - see getcode
* @return These are the possible return values:
* - NULL : no significant code
* - "recursive" : recursive function
* - "alloc" : the function returns allocated memory
* - "dealloc" : the function deallocates the variable
* - "dealloc_"
* - "use" : the variable is used (unknown usage of the variable => the checking bails out)
* - "callfunc" : a function call with unknown side effects
* - "&use"
*/
const char * call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &allocpar, unsigned int sz);
/**
* Extract a new tokens list that is easier to parse than the "_tokenizer->tokens()", the
* extracted tokens list describes how the given variable is used.
* The getcode and call_func are recursive
* @param tok start parse token
* @param callstack callstack
* @param varid variable id
* @param alloctype keep track of what type of allocation is used
* @param dealloctype keeps track of what type of deallocation is used
* @param classmember should be set if the inspected function is a class member
* @param sz size of type, used to check for mismatching size of allocation. for example "int *a;" => the sz is "sizeof(int)"
* @return Newly allocated token array. Caller needs to release reserved
* memory by calling TokenList::deleteTokens(returnValue);
* Returned tokens:
* - "alloc" : the variable is allocated
* - "assign" : the variable is assigned a new value
* - "break" : corresponds to "break"
* - "callfunc" : a function call with unknown side effects
* - "continue" : corresponds to "continue"
* - "dealloc" : the variable is deallocated
* - "goto" : corresponds to a "goto"
* - "if" : there is an "if"
* - "if(var)" : corresponds with "if ( var != 0 )"
* - "if(!var)" : corresponds with "if ( var == 0 )"
* - "ifv" : the variable is used in some way in a "if"
* - "loop" : corresponds to either a "for" or a "while"
* - "realloc" : the variable is reallocated
* - "return" : corresponds to a "return"
* - "use" : unknown usage -> bail out checking of this execution path
* - "&use" : the address of the variable is taken
* - "::use" : calling member function of class
* - "use_" : content of variable is accessed (used to warn access after dealloc)
*/
Token *getcode(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, unsigned int sz);
/**
* Simplify code e.g. by replacing empty "{ }" with ";"
* @param tok first token. The tokens list can be modified.
*/
void simplifycode(Token *tok) const;
static const Token *findleak(const Token *tokens);
/**
* Checking the variable varname
* @param startTok start token
* @param varname name of variable (for error messages)
* @param varid variable id
* @param classmember is the scope inside a class member function
* @param sz size of type.. if the variable is a "int *" then sz should be "sizeof(int)"
*/
void checkScope(const Token *startTok, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz);
private:
/** Report all possible errors (for the --errorlist) */
void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
CheckMemoryLeakInFunction c(0, settings, e);
c.memleakError(0, "varname");
c.resourceLeakError(0, "varname");
c.deallocDeallocError(0, "varname");
c.deallocuseError(0, "varname");
c.mismatchSizeError(0, "sz");
std::list<const Token *> callstack;
c.mismatchAllocDealloc(callstack, "varname");
c.memleakUponReallocFailureError(0, "varname");
}
/**
* Get name of class (--doc)
* @return name of class
*/
static std::string myName() {
return "Memory leaks (function variables)";
}
/**
* Get class information (--doc)
* @return Wiki formatted information about this class
*/
std::string classInfo() const {
return "Is there any allocated memory when a function goes out of scope\n";
}
const SymbolDatabase *symbolDatabase;
};
/**
* @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor
*/
class CPPCHECKLIB CheckMemoryLeakInClass : private Check, private CheckMemoryLeak {
public:
CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(0, 0, 0) {
}
CheckMemoryLeakInClass(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
: Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
}
void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
if (!tokenizr->isCPP())
return;
CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.check();
}
void check();
private:
void variable(const Scope *scope, const Token *tokVarname);
/** Public functions: possible double-allocation */
void checkPublicFunctions(const Scope *scope, const Token *classtok);
void publicAllocationError(const Token *tok, const std::string &varname);
void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname);
void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
CheckMemoryLeakInClass c(0, settings, e);
c.publicAllocationError(0, "varname");
c.unsafeClassError(0, "class", "class::varname");
}
static std::string myName() {
return "Memory leaks (class variables)";
}
std::string classInfo() const {
return "If the constructor allocate memory then the destructor must deallocate it.\n";
}
};
/** @brief detect simple memory leaks for struct members */
class CPPCHECKLIB CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak {
public:
CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(0, 0, 0) {
}
CheckMemoryLeakStructMember(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
: Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
}
void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
CheckMemoryLeakStructMember checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.check();
}
void check();
private:
/** Is local variable allocated with malloc? */
static bool isMalloc(const Variable *variable);
void checkStructVariable(const Variable * const variable);
void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const {
}
static std::string myName() {
return "Memory leaks (struct members)";
}
std::string classInfo() const {
return "Don't forget to deallocate struct members\n";
}
};
/** @brief detect simple memory leaks (address not taken) */
class CPPCHECKLIB CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak {
public:
CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(0, 0, 0) {
}
CheckMemoryLeakNoVar(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
: Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
}
void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
CheckMemoryLeakNoVar checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.check();
}
void check();
private:
/**
* @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned.
* @param scope The scope of the function to check.
*/
void checkForUnusedReturnValue(const Scope *scope);
/**
* @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr.
* @param scope The scope of the function to check.
*/
void checkForUnsafeArgAlloc(const Scope *scope);
void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
void returnValueNotUsedError(const Token* tok, const std::string &alloc);
void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType);
void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
CheckMemoryLeakNoVar c(0, settings, e);
c.functionCallLeak(0, "funcName", "funcName");
c.returnValueNotUsedError(0, "funcName");
c.unsafeArgAllocError(0, "funcName", "shared_ptr", "int");
}
static std::string myName() {
return "Memory leaks (address not taken)";
}
std::string classInfo() const {
return "Not taking the address to allocated memory\n";
}
};
/// @}
//---------------------------------------------------------------------------
#endif // checkmemoryleakH
Jump to Line
Something went wrong with that request. Please try again.