/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2009 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 //--------------------------------------------------------------------------- /// @addtogroup Checks /// @{ /** * Check for memory leaks * * The checking is split up into two 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. */ #include "check.h" #include <list> #include <string> #include <vector> class Token; /** @brief Base class for memory leaks checking */ class CheckMemoryLeak { private: const Tokenizer * const tokenizer; ErrorLogger * const errorLogger; /** 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 */ void reportErr(const Token *location, Severity::e severity, const std::string &id, const std::string &msg) 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 */ void reportErr(const std::list<const Token *> &callstack, Severity::e severity, const std::string &id, const std::string &msg) const; public: CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e) : tokenizer(t), errorLogger(e) { } /** What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Fd, Pipe, Dir, Many }; void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype, bool all); /** * Get type of deallocation at given position * @param tok position * @param varnames variable names * @return type of deallocation */ AllocType getDeallocationType(const Token *tok, const char *varnames[]) const; /** * 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; /** * Get type of allocation at given position */ AllocType getAllocationType(const Token *tok2, unsigned int varid) const; /** * Get type of reallocation at given position */ AllocType getReallocationType(const Token *tok2, unsigned int varid) const; bool isclass(const Tokenizer *_tokenizer, const Token *typestr) const; void memleakError(const Token *tok, const std::string &varname, bool all); void resourceLeakError(const Token *tok, const std::string &varname, bool all); void deallocDeallocError(const Token *tok, const std::string &varname); void deallocuseError(const Token *tok, const std::string &varname); void mismatchSizeError(const Token *tok, const std::string &sz); void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname); /** What type of allocated memory does the given function return? */ AllocType functionReturnType(const Token *tok) const; }; /** * @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 CheckMemoryLeakInFunction : private Check, private CheckMemoryLeak { public: /** This constructor is used when registering this class */ CheckMemoryLeakInFunction() : Check(), CheckMemoryLeak(0, 0) { } /** This constructor is used when running checks */ CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger) { } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); #ifndef _MSC_VER private: #endif bool matchFunctionsThatReturnArg(const Token *tok, unsigned int varid) const; /** * Check if there is a "!var" match inside a condition * @param tok first token to match * @param varid variabla id * @param endpar if this is true the "!var" must be followed by ")" * @return true if match */ bool notvar(const Token *tok, unsigned int varid, bool endpar = false) const; /** * 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 all is the code simplified according to the "--all" rules or not? * @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" * - "&use" */ const char * call_func(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool &all, 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 all has the getcode been simplified according to the "--all" rules? * @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 Tokenizer::deleteTokens(returnValue); * Returned tokens: * - alloc : the variable is allocated * - dealloc : the variable is deallocated * - realloc : the variable is reallocated * - use : unknown usage -> bail out checking of this execution path * - &use : the address of the variable is taken * - ::use : calling member function of class * - assign : the variable is assigned a new value * - 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" * - continue : corresponds to "continue" * - break : corresponds to "break" * - goto : corresponds to a "goto" * - return : corresponds to a "return" */ Token *getcode(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, bool &all, unsigned int sz); /** * Simplify code e.g. by replacing empty "{ }" with ";" * @param tok first token. The tokens list can be modified. * @param all is the code simplified according to the "--all" rules or not */ void simplifycode(Token *tok, bool &all); static const Token *findleak(const Token *tokens, bool all); /** * Checking the variable varname * @param Tok1 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 *Tok1, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz); void getErrorMessages() { memleakError(0, "varname", false); resourceLeakError(0, "varname", false); deallocDeallocError(0, "varname"); deallocuseError(0, "varname"); mismatchSizeError(0, "sz"); std::list<const Token *> callstack; mismatchAllocDealloc(callstack, "varname"); } std::string name() const { return "Memory leaks (function variables)"; } std::string classInfo() const { return "Is there any allocated memory when a function goes out of scope"; } }; /** * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor */ class CheckMemoryLeakInClass : private Check, private CheckMemoryLeak { public: CheckMemoryLeakInClass() : Check(), CheckMemoryLeak(0, 0) { } CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger) { } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckMemoryLeakInClass checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: void parseClass(const Token *tok1, std::vector<const char *> &classname); void variable(const char classname[], const Token *tokVarname); void getErrorMessages() { } std::string name() const { return "Memory leaks (class variables)"; } std::string classInfo() const { return "If the constructor allocate memory then the destructor must deallocate it."; } }; /** @brief detect simple memory leaks for struct members */ class CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak { public: CheckMemoryLeakStructMember() : Check(), CheckMemoryLeak(0, 0) { } CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger) { } void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckMemoryLeakStructMember checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: void getErrorMessages() { } std::string name() const { return "Memory leaks (struct members)"; } std::string classInfo() const { return "Don't forget to deallocate struct members"; } }; /// @} //--------------------------------------------------------------------------- #endif