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