|
Cppcheck
|
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
1.7.6.1