Cppcheck
checkmemoryleak.h
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 
00020 
00021 //---------------------------------------------------------------------------
00022 #ifndef checkmemoryleakH
00023 #define checkmemoryleakH
00024 //---------------------------------------------------------------------------
00025 
00026 /**
00027  * @file
00028  *
00029  * %Check for memory leaks
00030  *
00031  * The checking is split up into three specialized classes.
00032  * - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly.
00033  * - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly.
00034  * - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members
00035  */
00036 
00037 #include "config.h"
00038 #include "check.h"
00039 
00040 #include <list>
00041 #include <string>
00042 
00043 class Token;
00044 class Scope;
00045 class Function;
00046 class Variable;
00047 
00048 /// @addtogroup Core
00049 /// @{
00050 
00051 /** @brief Base class for memory leaks checking */
00052 class CPPCHECKLIB CheckMemoryLeak {
00053 private:
00054     /** For access to the tokens */
00055     const Tokenizer * const tokenizer;
00056 
00057     /** ErrorLogger used to report errors */
00058     ErrorLogger * const errorLogger;
00059 
00060     /** Enabled standards */
00061     const Standards & standards;
00062 
00063     /** Disable the default constructors */
00064     CheckMemoryLeak();
00065 
00066     /** Disable the default constructors */
00067     CheckMemoryLeak(const CheckMemoryLeak &);
00068 
00069     /** disable assignment operator */
00070     void operator=(const CheckMemoryLeak &);
00071 
00072     /**
00073      * Report error. Similar with the function Check::reportError
00074      * @param location the token where the error occurs
00075      * @param severity the severity of the bug
00076      * @param id type of message
00077      * @param msg text
00078      */
00079     void reportErr(const Token *location, Severity::SeverityType severity, const std::string &id, const std::string &msg) const;
00080 
00081     /**
00082      * Report error. Similar with the function Check::reportError
00083      * @param callstack callstack of error
00084      * @param severity the severity of the bug
00085      * @param id type of message
00086      * @param msg text
00087      */
00088     void reportErr(const std::list<const Token *> &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg) const;
00089 
00090 public:
00091     CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Standards &s)
00092         : tokenizer(t), errorLogger(e), standards(s) {
00093     }
00094 
00095     /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
00096     enum AllocType { No, Malloc, gMalloc, New, NewArray, File, Fd, Pipe, Dir, Many };
00097 
00098     void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype);
00099 
00100     /**
00101      * @brief Get type of deallocation at given position
00102      * @param tok position
00103      * @param varname variable name
00104      * @return type of deallocation
00105      */
00106     AllocType getDeallocationType(const Token *tok, const std::string &varname) const;
00107 
00108     /**
00109      * @brief Get type of deallocation at given position
00110      * @param tok position
00111      * @param varid variable id
00112      * @return type of deallocation
00113      */
00114     AllocType getDeallocationType(const Token *tok, unsigned int varid) const;
00115 
00116     /**
00117      * @brief Get type of allocation at given position
00118      */
00119     AllocType getAllocationType(const Token *tok2, unsigned int varid, std::list<const Function*> *callstack = NULL) const;
00120 
00121     /**
00122      * @brief Get type of reallocation at given position
00123      */
00124     AllocType getReallocationType(const Token *tok2, unsigned int varid) const;
00125 
00126     /**
00127      * @brief Is a typename the name of a class?
00128      * @param tok type token
00129      * @param varid variable id
00130      * @return true if the type name is the name of a class
00131      */
00132     bool isclass(const Token *tok, unsigned int varid) const;
00133 
00134     /**
00135      * Report that there is a memory leak (new/malloc/etc)
00136      * @param tok token where memory is leaked
00137      * @param varname name of variable
00138      */
00139     void memleakError(const Token *tok, const std::string &varname) const;
00140 
00141     /**
00142      * Report that there is a resource leak (fopen/popen/etc)
00143      * @param tok token where resource is leaked
00144      * @param varname name of variable
00145      */
00146     void resourceLeakError(const Token *tok, const std::string &varname) const;
00147 
00148     /**
00149      * @brief Report error: deallocating a deallocated pointer
00150      * @param tok token where error occurs
00151      * @param varname name of variable
00152      */
00153     void deallocDeallocError(const Token *tok, const std::string &varname) const;
00154     void deallocuseError(const Token *tok, const std::string &varname) const;
00155     void mismatchSizeError(const Token *tok, const std::string &sz) const;
00156     void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname) const;
00157     void memleakUponReallocFailureError(const Token *tok, const std::string &varname) const;
00158 
00159     /** What type of allocated memory does the given function return? */
00160     AllocType functionReturnType(const Function* func, std::list<const Function*> *callstack = NULL) const;
00161 
00162     /** Function allocates pointed-to argument (a la asprintf)? */
00163     const char *functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const;
00164 };
00165 
00166 /// @}
00167 
00168 
00169 
00170 /// @addtogroup Checks
00171 /// @{
00172 
00173 
00174 /**
00175  * @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly.
00176  *
00177  * The checking is done by looking at each function variable separately. By repeating these 4 steps over and over:
00178  * -# locate a function variable
00179  * -# create a simple token list that describes the usage of the function variable.
00180  * -# simplify the token list.
00181  * -# finally, check if the simplified token list contain any leaks.
00182  */
00183 
00184 class CPPCHECKLIB CheckMemoryLeakInFunction : private Check, public CheckMemoryLeak {
00185 public:
00186     /** @brief This constructor is used when registering this class */
00187     CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(0, 0, Standards()), symbolDatabase(NULL)
00188     { }
00189 
00190     /** @brief This constructor is used when running checks */
00191     CheckMemoryLeakInFunction(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
00192         : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings->standards) {
00193         // get the symbol database
00194         if (tokenizr)
00195             symbolDatabase = tokenizr->getSymbolDatabase();
00196         else
00197             symbolDatabase = 0;
00198     }
00199 
00200     /** @brief run all simplified checks */
00201     void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
00202         CheckMemoryLeakInFunction checkMemoryLeak(tokenizr, settings, errLog);
00203         checkMemoryLeak.checkReallocUsage();
00204         checkMemoryLeak.check();
00205     }
00206 
00207     /** @brief Unit testing : testing the white list */
00208     static bool test_white_list(const std::string &funcname);
00209 
00210     /** @brief Perform checking */
00211     void check();
00212 
00213     /**
00214      * Checking for a memory leak caused by improper realloc usage.
00215      */
00216     void checkReallocUsage();
00217 
00218     /**
00219      * @brief %Check if there is a "!var" match inside a condition
00220      * @param tok      first token to match
00221      * @param varid    variable id
00222      * @param endpar   if this is true the "!var" must be followed by ")"
00223      * @return true if match
00224      */
00225     static bool notvar(const Token *tok, unsigned int varid, bool endpar = false);
00226 
00227     /**
00228      * Inspect a function call. the call_func and getcode are recursive
00229      * @param tok          token where the function call occurs
00230      * @param callstack    callstack
00231      * @param varid        variable id to check
00232      * @param alloctype    if memory is allocated, this indicates the type of allocation
00233      * @param dealloctype  if memory is deallocated, this indicates the type of deallocation
00234      * @param allocpar     if function allocates varid parameter
00235      * @param sz           not used by call_func - see getcode
00236      * @return These are the possible return values:
00237      * - NULL : no significant code
00238      * - "recursive" : recursive function
00239      * - "alloc" : the function returns allocated memory
00240      * - "dealloc" : the function deallocates the variable
00241      * - "dealloc_"
00242      * - "use" : the variable is used (unknown usage of the variable => the checking bails out)
00243      * - "callfunc" : a function call with unknown side effects
00244      * - "&use"
00245      */
00246     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);
00247 
00248     /**
00249      * Extract a new tokens list that is easier to parse than the "_tokenizer->tokens()", the
00250      * extracted tokens list describes how the given variable is used.
00251      * The getcode and call_func are recursive
00252      * @param tok start parse token
00253      * @param callstack callstack
00254      * @param varid variable id
00255      * @param alloctype keep track of what type of allocation is used
00256      * @param dealloctype keeps track of what type of deallocation is used
00257      * @param classmember should be set if the inspected function is a class member
00258      * @param sz size of type, used to check for mismatching size of allocation. for example "int *a;" => the sz is "sizeof(int)"
00259      * @return Newly allocated token array. Caller needs to release reserved
00260      * memory by calling TokenList::deleteTokens(returnValue);
00261      * Returned tokens:
00262      * - "alloc" : the variable is allocated
00263      * - "assign" : the variable is assigned a new value
00264      * - "break" : corresponds to "break"
00265      * - "callfunc" : a function call with unknown side effects
00266      * - "continue" : corresponds to "continue"
00267      * - "dealloc" : the variable is deallocated
00268      * - "goto" : corresponds to a "goto"
00269      * - "if" : there is an "if"
00270      * - "if(var)" : corresponds with "if ( var != 0 )"
00271      * - "if(!var)" : corresponds with "if ( var == 0 )"
00272      * - "ifv" : the variable is used in some way in a "if"
00273      * - "loop" : corresponds to either a "for" or a "while"
00274      * - "realloc" : the variable is reallocated
00275      * - "return" : corresponds to a "return"
00276      * - "use" : unknown usage -> bail out checking of this execution path
00277      * - "&use" : the address of the variable is taken
00278      * - "::use" : calling member function of class
00279      * - "use_" : content of variable is accessed (used to warn access after dealloc)
00280      */
00281     Token *getcode(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, unsigned int sz);
00282 
00283     /**
00284      * Simplify code e.g. by replacing empty "{ }" with ";"
00285      * @param tok first token. The tokens list can be modified.
00286      */
00287     void simplifycode(Token *tok) const;
00288 
00289     static const Token *findleak(const Token *tokens);
00290 
00291     /**
00292      * Checking the variable varname
00293      * @param Tok1 start token
00294      * @param varname name of variable (for error messages)
00295      * @param varid variable id
00296      * @param classmember is the scope inside a class member function
00297      * @param sz size of type.. if the variable is a "int *" then sz should be "sizeof(int)"
00298      */
00299     void checkScope(const Token *Tok1, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz);
00300 
00301     /** parse tokens to see what functions are "noreturn" */
00302     void parse_noreturn();
00303 
00304 private:
00305     /** Report all possible errors (for the --errorlist) */
00306     void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
00307         CheckMemoryLeakInFunction c(0, settings, e);
00308 
00309         c.memleakError(0, "varname");
00310         c.resourceLeakError(0, "varname");
00311 
00312         c.deallocDeallocError(0, "varname");
00313         c.deallocuseError(0, "varname");
00314         c.mismatchSizeError(0, "sz");
00315         std::list<const Token *> callstack;
00316         c.mismatchAllocDealloc(callstack, "varname");
00317         c.memleakUponReallocFailureError(0, "varname");
00318     }
00319 
00320     /**
00321      * Get name of class (--doc)
00322      * @return name of class
00323      */
00324     static std::string myName() {
00325         return "Memory leaks (function variables)";
00326     }
00327 
00328     /**
00329      * Get class information (--doc)
00330      * @return Wiki formatted information about this class
00331      */
00332     std::string classInfo() const {
00333         return "Is there any allocated memory when a function goes out of scope\n";
00334     }
00335 
00336     /** Function names for functions that are "noreturn" */
00337     std::set<std::string> noreturn;
00338 
00339     /** Function names for functions that are not "noreturn" */
00340     std::set<std::string> notnoreturn;
00341 
00342     const SymbolDatabase *symbolDatabase;
00343 };
00344 
00345 
00346 
00347 /**
00348  * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor
00349  */
00350 
00351 class CPPCHECKLIB CheckMemoryLeakInClass : private Check, private CheckMemoryLeak {
00352 public:
00353     CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(0, 0, Standards())
00354     { }
00355 
00356     CheckMemoryLeakInClass(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
00357         : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings->standards)
00358     { }
00359 
00360     void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
00361         if (!tokenizr->isCPP())
00362             return;
00363 
00364         CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog);
00365         checkMemoryLeak.check();
00366     }
00367 
00368     void check();
00369 
00370 private:
00371     void variable(const Scope *scope, const Token *tokVarname);
00372 
00373     /** Public functions: possible double-allocation */
00374     void checkPublicFunctions(const Scope *scope, const Token *classtok);
00375     void publicAllocationError(const Token *tok, const std::string &varname);
00376 
00377     void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname);
00378 
00379     void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
00380         CheckMemoryLeakInClass c(0, settings, e);
00381         c.publicAllocationError(0, "varname");
00382         c.unsafeClassError(0, "class", "class::varname");
00383     }
00384 
00385     static std::string myName() {
00386         return "Memory leaks (class variables)";
00387     }
00388 
00389     std::string classInfo() const {
00390         return "If the constructor allocate memory then the destructor must deallocate it.\n";
00391     }
00392 };
00393 
00394 
00395 
00396 /** @brief detect simple memory leaks for struct members */
00397 
00398 class CPPCHECKLIB CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak {
00399 public:
00400     CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(0, 0, Standards())
00401     { }
00402 
00403     CheckMemoryLeakStructMember(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
00404         : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings->standards)
00405     { }
00406 
00407     void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
00408         CheckMemoryLeakStructMember checkMemoryLeak(tokenizr, settings, errLog);
00409         checkMemoryLeak.check();
00410     }
00411 
00412     void check();
00413 
00414 private:
00415 
00416     /** Is local variable allocated with malloc? */
00417     static bool isMalloc(const Variable *variable);
00418 
00419     void checkStructVariable(const Variable * const variable);
00420 
00421     void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const
00422     { }
00423 
00424     static std::string myName() {
00425         return "Memory leaks (struct members)";
00426     }
00427 
00428     std::string classInfo() const {
00429         return "Don't forget to deallocate struct members\n";
00430     }
00431 };
00432 
00433 
00434 
00435 /** @brief detect simple memory leaks (address not taken) */
00436 
00437 class CPPCHECKLIB CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak {
00438 public:
00439     CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(0, 0, Standards())
00440     { }
00441 
00442     CheckMemoryLeakNoVar(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
00443         : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings->standards)
00444     { }
00445 
00446     void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
00447         CheckMemoryLeakNoVar checkMemoryLeak(tokenizr, settings, errLog);
00448         checkMemoryLeak.check();
00449     }
00450 
00451     void check();
00452 
00453 private:
00454 
00455     void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
00456 
00457     void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
00458         CheckMemoryLeakNoVar c(0, settings, e);
00459 
00460         c.functionCallLeak(0, "funcName", "funcName");
00461     }
00462 
00463     static std::string myName() {
00464         return "Memory leaks (address not taken)";
00465     }
00466 
00467     std::string classInfo() const {
00468         return "Not taking the address to allocated memory\n";
00469     }
00470 };
00471 /// @}
00472 //---------------------------------------------------------------------------
00473 #endif