Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
checkmemoryleak.h
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2014 Daniel Marjamäki and Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 //---------------------------------------------------------------------------
20 #ifndef checkmemoryleakH
21 #define checkmemoryleakH
22 //---------------------------------------------------------------------------
23 
24 /**
25  * @file
26  *
27  * %Check for memory leaks
28  *
29  * The checking is split up into three specialized classes.
30  * - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly.
31  * - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly.
32  * - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members
33  */
34 
35 #include "config.h"
36 #include "check.h"
37 
38 #include <list>
39 #include <string>
40 
41 class Token;
42 class Scope;
43 class Function;
44 class Variable;
45 
46 /// @addtogroup Core
47 /// @{
48 
49 /** @brief Base class for memory leaks checking */
51 private:
52  /** For access to the tokens */
53  const Tokenizer * const tokenizer;
54 
55  /** ErrorLogger used to report errors */
57 
58  /** Enabled standards */
59  const Settings * const settings1;
60 
61  /** Disable the default constructors */
63 
64  /** Disable the default constructors */
66 
67  /** disable assignment operator */
68  void operator=(const CheckMemoryLeak &);
69 
70  /**
71  * Report error. Similar with the function Check::reportError
72  * @param location the token where the error occurs
73  * @param severity the severity of the bug
74  * @param id type of message
75  * @param msg text
76  */
77  void reportErr(const Token *location, Severity::SeverityType severity, const std::string &id, const std::string &msg) const;
78 
79  /**
80  * Report error. Similar with the function Check::reportError
81  * @param callstack callstack of error
82  * @param severity the severity of the bug
83  * @param id type of message
84  * @param msg text
85  */
86  void reportErr(const std::list<const Token *> &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg) const;
87 
88 public:
90  : tokenizer(t), errorLogger(e), settings1(s) {
91  }
92 
93  /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
94  enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many };
95 
96  void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype);
97 
98  /**
99  * @brief Get type of deallocation at given position
100  * @param tok position
101  * @param varname variable name
102  * @return type of deallocation
103  */
104  AllocType getDeallocationType(const Token *tok, const std::string &varname) const;
105 
106  /**
107  * @brief Get type of deallocation at given position
108  * @param tok position
109  * @param varid variable id
110  * @return type of deallocation
111  */
112  AllocType getDeallocationType(const Token *tok, unsigned int varid) const;
113 
114  /**
115  * @brief Get type of allocation at given position
116  */
117  AllocType getAllocationType(const Token *tok2, unsigned int varid, std::list<const Function*> *callstack = NULL) const;
118 
119  /**
120  * @brief Get type of reallocation at given position
121  */
122  static AllocType getReallocationType(const Token *tok2, unsigned int varid);
123 
124  /**
125  * @brief Is a typename the name of a class?
126  * @param tok type token
127  * @param varid variable id
128  * @return true if the type name is the name of a class
129  */
130  bool isclass(const Token *tok, unsigned int varid) const;
131 
132  /**
133  * Report that there is a memory leak (new/malloc/etc)
134  * @param tok token where memory is leaked
135  * @param varname name of variable
136  */
137  void memleakError(const Token *tok, const std::string &varname) const;
138 
139  /**
140  * Report that there is a resource leak (fopen/popen/etc)
141  * @param tok token where resource is leaked
142  * @param varname name of variable
143  */
144  void resourceLeakError(const Token *tok, const std::string &varname) const;
145 
146  /**
147  * @brief Report error: deallocating a deallocated pointer
148  * @param tok token where error occurs
149  * @param varname name of variable
150  */
151  void deallocDeallocError(const Token *tok, const std::string &varname) const;
152  void deallocuseError(const Token *tok, const std::string &varname) const;
153  void mismatchSizeError(const Token *tok, const std::string &sz) const;
154  void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname) const;
155  void memleakUponReallocFailureError(const Token *tok, const std::string &varname) const;
156 
157  /** What type of allocated memory does the given function return? */
158  AllocType functionReturnType(const Function* func, std::list<const Function*> *callstack = NULL) const;
159 
160  /** Function allocates pointed-to argument (a la asprintf)? */
161  const char *functionArgAlloc(const Function *func, unsigned int targetpar, AllocType &allocType) const;
162 };
163 
164 /// @}
165 
166 
167 
168 /// @addtogroup Checks
169 /// @{
170 
171 
172 /**
173  * @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly.
174  *
175  * The checking is done by looking at each function variable separately. By repeating these 4 steps over and over:
176  * -# locate a function variable
177  * -# create a simple token list that describes the usage of the function variable.
178  * -# simplify the token list.
179  * -# finally, check if the simplified token list contain any leaks.
180  */
181 
183 public:
184  /** @brief This constructor is used when registering this class */
185  CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(0, 0, 0), symbolDatabase(NULL) {
186  }
187 
188  /** @brief This constructor is used when running checks */
189  CheckMemoryLeakInFunction(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
190  : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
191  // get the symbol database
192  if (tokenizr)
193  symbolDatabase = tokenizr->getSymbolDatabase();
194  else
195  symbolDatabase = 0;
196  }
197 
198  /** @brief run all simplified checks */
199  void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
200  CheckMemoryLeakInFunction checkMemoryLeak(tokenizr, settings, errLog);
201  checkMemoryLeak.checkReallocUsage();
202  checkMemoryLeak.check();
203  }
204 
205  /** @brief Unit testing : testing the white list */
206  static bool test_white_list(const std::string &funcname);
207  static bool test_white_list_with_lib(const std::string &funcname, const Settings *settings);
208 
209  /** @brief Perform checking */
210  void check();
211 
212  /**
213  * Checking for a memory leak caused by improper realloc usage.
214  */
215  void checkReallocUsage();
216 
217  /**
218  * @brief %Check if there is a "!var" match inside a condition
219  * @param tok first token to match
220  * @param varid variable id
221  * @param endpar if this is true the "!var" must be followed by ")"
222  * @return true if match
223  */
224  static bool notvar(const Token *tok, unsigned int varid, bool endpar = false);
225 
226  /**
227  * Inspect a function call. the call_func and getcode are recursive
228  * @param tok token where the function call occurs
229  * @param callstack callstack
230  * @param varid variable id to check
231  * @param alloctype if memory is allocated, this indicates the type of allocation
232  * @param dealloctype if memory is deallocated, this indicates the type of deallocation
233  * @param allocpar if function allocates varid parameter
234  * @param sz not used by call_func - see getcode
235  * @return These are the possible return values:
236  * - NULL : no significant code
237  * - "recursive" : recursive function
238  * - "alloc" : the function returns allocated memory
239  * - "dealloc" : the function deallocates the variable
240  * - "dealloc_"
241  * - "use" : the variable is used (unknown usage of the variable => the checking bails out)
242  * - "callfunc" : a function call with unknown side effects
243  * - "&use"
244  */
245  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);
246 
247  /**
248  * Extract a new tokens list that is easier to parse than the "_tokenizer->tokens()", the
249  * extracted tokens list describes how the given variable is used.
250  * The getcode and call_func are recursive
251  * @param tok start parse token
252  * @param callstack callstack
253  * @param varid variable id
254  * @param alloctype keep track of what type of allocation is used
255  * @param dealloctype keeps track of what type of deallocation is used
256  * @param classmember should be set if the inspected function is a class member
257  * @param sz size of type, used to check for mismatching size of allocation. for example "int *a;" => the sz is "sizeof(int)"
258  * @return Newly allocated token array. Caller needs to release reserved
259  * memory by calling TokenList::deleteTokens(returnValue);
260  * Returned tokens:
261  * - "alloc" : the variable is allocated
262  * - "assign" : the variable is assigned a new value
263  * - "break" : corresponds to "break"
264  * - "callfunc" : a function call with unknown side effects
265  * - "continue" : corresponds to "continue"
266  * - "dealloc" : the variable is deallocated
267  * - "goto" : corresponds to a "goto"
268  * - "if" : there is an "if"
269  * - "if(var)" : corresponds with "if ( var != 0 )"
270  * - "if(!var)" : corresponds with "if ( var == 0 )"
271  * - "ifv" : the variable is used in some way in a "if"
272  * - "loop" : corresponds to either a "for" or a "while"
273  * - "realloc" : the variable is reallocated
274  * - "return" : corresponds to a "return"
275  * - "use" : unknown usage -> bail out checking of this execution path
276  * - "&use" : the address of the variable is taken
277  * - "::use" : calling member function of class
278  * - "use_" : content of variable is accessed (used to warn access after dealloc)
279  */
280  Token *getcode(const Token *tok, std::list<const Token *> callstack, const unsigned int varid, AllocType &alloctype, AllocType &dealloctype, bool classmember, unsigned int sz);
281 
282  /**
283  * Simplify code e.g. by replacing empty "{ }" with ";"
284  * @param tok first token. The tokens list can be modified.
285  */
286  void simplifycode(Token *tok) const;
287 
288  static const Token *findleak(const Token *tokens);
289 
290  /**
291  * Checking the variable varname
292  * @param Tok1 start token
293  * @param varname name of variable (for error messages)
294  * @param varid variable id
295  * @param classmember is the scope inside a class member function
296  * @param sz size of type.. if the variable is a "int *" then sz should be "sizeof(int)"
297  */
298  void checkScope(const Token *Tok1, const std::string &varname, unsigned int varid, bool classmember, unsigned int sz);
299 
300  /** parse tokens to see what functions are "noreturn" */
301  void parse_noreturn();
302 
303 private:
304  /** Report all possible errors (for the --errorlist) */
305  void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
306  CheckMemoryLeakInFunction c(0, settings, e);
307 
308  c.memleakError(0, "varname");
309  c.resourceLeakError(0, "varname");
310 
311  c.deallocDeallocError(0, "varname");
312  c.deallocuseError(0, "varname");
313  c.mismatchSizeError(0, "sz");
314  std::list<const Token *> callstack;
315  c.mismatchAllocDealloc(callstack, "varname");
316  c.memleakUponReallocFailureError(0, "varname");
317  }
318 
319  /**
320  * Get name of class (--doc)
321  * @return name of class
322  */
323  static std::string myName() {
324  return "Memory leaks (function variables)";
325  }
326 
327  /**
328  * Get class information (--doc)
329  * @return Wiki formatted information about this class
330  */
331  std::string classInfo() const {
332  return "Is there any allocated memory when a function goes out of scope\n";
333  }
334 
335  /** Function names for functions that are "noreturn" */
336  std::set<std::string> noreturn;
337 
338  /** Function names for functions that are not "noreturn" */
339  std::set<std::string> notnoreturn;
340 
342 };
343 
344 
345 
346 /**
347  * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor
348  */
349 
351 public:
352  CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(0, 0, 0) {
353  }
354 
355  CheckMemoryLeakInClass(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
356  : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
357  }
358 
359  void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
360  if (!tokenizr->isCPP())
361  return;
362 
363  CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog);
364  checkMemoryLeak.check();
365  }
366 
367  void check();
368 
369 private:
370  void variable(const Scope *scope, const Token *tokVarname);
371 
372  /** Public functions: possible double-allocation */
373  void checkPublicFunctions(const Scope *scope, const Token *classtok);
374  void publicAllocationError(const Token *tok, const std::string &varname);
375 
376  void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname);
377 
378  void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
379  CheckMemoryLeakInClass c(0, settings, e);
380  c.publicAllocationError(0, "varname");
381  c.unsafeClassError(0, "class", "class::varname");
382  }
383 
384  static std::string myName() {
385  return "Memory leaks (class variables)";
386  }
387 
388  std::string classInfo() const {
389  return "If the constructor allocate memory then the destructor must deallocate it.\n";
390  }
391 };
392 
393 
394 
395 /** @brief detect simple memory leaks for struct members */
396 
398 public:
400  }
401 
402  CheckMemoryLeakStructMember(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
403  : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
404  }
405 
406  void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
407  CheckMemoryLeakStructMember checkMemoryLeak(tokenizr, settings, errLog);
408  checkMemoryLeak.check();
409  }
410 
411  void check();
412 
413 private:
414 
415  /** Is local variable allocated with malloc? */
416  static bool isMalloc(const Variable *variable);
417 
418  void checkStructVariable(const Variable * const variable);
419 
420  void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const {
421  }
422 
423  static std::string myName() {
424  return "Memory leaks (struct members)";
425  }
426 
427  std::string classInfo() const {
428  return "Don't forget to deallocate struct members\n";
429  }
430 };
431 
432 
433 
434 /** @brief detect simple memory leaks (address not taken) */
435 
437 public:
438  CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(0, 0, 0) {
439  }
440 
441  CheckMemoryLeakNoVar(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog)
442  : Check(myName(), tokenizr, settings, errLog), CheckMemoryLeak(tokenizr, errLog, settings) {
443  }
444 
445  void runSimplifiedChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) {
446  CheckMemoryLeakNoVar checkMemoryLeak(tokenizr, settings, errLog);
447  checkMemoryLeak.check();
448  }
449 
450  void check();
451 
452 private:
453  /**
454  * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned.
455  * @param scope The scope of the function to check.
456  */
457  void checkForUnusedReturnValue(const Scope *scope);
458 
459  void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
460  void returnValueNotUsedError(const Token* tok, const std::string &alloc);
461 
462  void getErrorMessages(ErrorLogger *e, const Settings *settings) const {
463  CheckMemoryLeakNoVar c(0, settings, e);
464 
465  c.functionCallLeak(0, "funcName", "funcName");
466  c.returnValueNotUsedError(0, "funcName");
467  }
468 
469  static std::string myName() {
470  return "Memory leaks (address not taken)";
471  }
472 
473  std::string classInfo() const {
474  return "Not taking the address to allocated memory\n";
475  }
476 };
477 /// @}
478 //---------------------------------------------------------------------------
479 #endif // checkmemoryleakH