Cppcheck
checkleakautovar.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 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 // Leaks when using auto variables
21 //---------------------------------------------------------------------------
22 
23 #include "checkleakautovar.h"
24 
25 #include "astutils.h"
26 #include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
27 #include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
28 #include "mathlib.h"
29 #include "platform.h"
30 #include "settings.h"
31 #include "errortypes.h"
32 #include "symboldatabase.h"
33 #include "token.h"
34 #include "tokenize.h"
35 #include "tokenlist.h"
36 #include "utils.h"
37 #include "vfvalue.h"
38 
39 #include <algorithm>
40 #include <array>
41 #include <cstddef>
42 #include <iostream>
43 #include <list>
44 #include <vector>
45 
46 //---------------------------------------------------------------------------
47 
48 // Register this check class (by creating a static instance of it)
49 namespace {
50  CheckLeakAutoVar instance;
51 }
52 
53 static const CWE CWE672(672U);
54 static const CWE CWE415(415U);
55 
56 // Hardcoded allocation types (not from library)
57 static constexpr int NEW_ARRAY = -2;
58 static constexpr int NEW = -1;
59 
60 static const std::array<std::pair<std::string, std::string>, 4> alloc_failed_conds {{{"==", "0"}, {"<", "0"}, {"==", "-1"}, {"<=", "-1"}}};
61 static const std::array<std::pair<std::string, std::string>, 4> alloc_success_conds {{{"!=", "0"}, {">", "0"}, {"!=", "-1"}, {">=", "0"}}};
62 
63 static bool isAutoDeallocType(const Type* type) {
64  if (!type || !type->classScope)
65  return true;
66  if (type->classScope->numConstructors > 0)
67  return true;
68  const std::list<Variable>& varlist = type->classScope->varlist;
69  if (std::any_of(varlist.begin(), varlist.end(), [](const Variable& v) {
70  return !v.valueType() || (!v.valueType()->isPrimitive() && !v.valueType()->container);
71  }))
72  return true;
73  if (std::none_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [](const Type::BaseInfo& bi) {
74  return isAutoDeallocType(bi.type);
75  }))
76  return false;
77  return true;
78 }
79 
80 /**
81  * @brief Is variable type some class with automatic deallocation?
82  * @param var variable token
83  * @return true unless it can be seen there is no automatic deallocation
84  */
85 static bool isAutoDealloc(const Variable *var)
86 {
87  if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE)
88  return false;
89 
90  // return false if the type is a simple record type without side effects
91  // a type that has no side effects (no constructors and no members with constructors)
92  /** @todo false negative: check constructors for side effects */
93  return isAutoDeallocType(var->type());
94 }
95 
96 template<std::size_t N>
97 static bool isVarTokComparison(const Token * tok, const Token ** vartok,
98  const std::array<std::pair<std::string, std::string>, N>& ops)
99 {
100  return std::any_of(ops.cbegin(), ops.cend(), [&](const std::pair<std::string, std::string>& op) {
101  return astIsVariableComparison(tok, op.first, op.second, vartok);
102  });
103 }
104 
105 //---------------------------------------------------------------------------
106 
108 {
109  std::cout << "size=" << alloctype.size() << std::endl;
110  for (std::map<int, AllocInfo>::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) {
111  std::string strusage;
112  const auto use = possibleUsage.find(it->first);
113  if (use != possibleUsage.end())
114  strusage = use->second.first->str();
115 
116  std::string status;
117  switch (it->second.status) {
118  case OWNED:
119  status = "owned";
120  break;
121  case DEALLOC:
122  status = "dealloc";
123  break;
124  case ALLOC:
125  status = "alloc";
126  break;
127  case NOALLOC:
128  status = "noalloc";
129  break;
130  case REALLOC:
131  status = "realloc";
132  break;
133  default:
134  status = "?";
135  break;
136  }
137 
138  std::cout << "status=" << status << " "
139  << "alloctype='" << it->second.type << "' "
140  << "possibleUsage='" << strusage << "' "
141  << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " "
142  << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " "
143  << "reallocedFrom=" << it->second.reallocedFromType
144  << std::endl;
145  }
146 }
147 
148 void VarInfo::possibleUsageAll(const std::pair<const Token*, Usage>& functionUsage)
149 {
150  possibleUsage.clear();
151  for (std::map<int, AllocInfo>::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it)
152  possibleUsage[it->first] = functionUsage;
153 }
154 
155 
156 void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) const
157 {
158  const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings);
159  if (Library::isresource(type))
160  checkmemleak.resourceLeakError(tok, varname);
161  else
162  checkmemleak.memleakError(tok, varname);
163 }
164 
165 void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const
166 {
168  const std::list<const Token *> callstack = { allocTok, deallocTok };
169  c.mismatchAllocDealloc(callstack, varname);
170 }
171 
172 void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) const
173 {
175  c.deallocuseError(tok, varname);
176 }
177 
178 void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname)
179 {
180  const std::list<const Token *> locations = { deallocTok, tok };
181  reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal);
182 }
183 
184 void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair<const Token*, VarInfo::Usage>& functionUsage)
185 {
186  if (mSettings->checkLibrary && functionUsage.second == VarInfo::USED &&
187  (!functionUsage.first || !functionUsage.first->function() || !functionUsage.first->function()->hasBody())) {
188  std::string funcStr = functionUsage.first ? mSettings->library.getFunctionName(functionUsage.first) : "f";
189  if (funcStr.empty())
190  funcStr = "unknown::" + functionUsage.first->str();
191  reportError(tok,
193  "checkLibraryUseIgnore",
194  "--check-library: Function " + funcStr + "() should have <use>/<leak-ignore> configuration");
195  }
196 }
197 
198 void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type)
199 {
200  const std::list<const Token *> locations = { prevFreeTok, tok };
201 
202  if (Library::isresource(type))
203  reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, Certainty::normal);
204  else
205  reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, Certainty::normal);
206 }
207 
208 
210 {
211  if (mSettings->clang)
212  return;
213 
214  logChecker("CheckLeakAutoVar::check"); // notclang
215 
216  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
217 
218  // Local variables that are known to be non-zero.
219  const std::set<int> notzero;
220 
221  // Check function scopes
222  for (const Scope * scope : symbolDatabase->functionScopes) {
223  if (scope->hasInlineOrLambdaFunction())
224  continue;
225 
226  // Empty variable info
227  VarInfo varInfo;
228 
229  checkScope(scope->bodyStart, varInfo, notzero, 0);
230  }
231 }
232 
233 static bool isVarUsedInTree(const Token *tok, nonneg int varid)
234 {
235  if (!tok)
236  return false;
237  if (tok->varId() == varid)
238  return true;
239  if (tok->str() == "(" && Token::simpleMatch(tok->astOperand1(), "sizeof"))
240  return false;
241  return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid);
242 }
243 
244 static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid)
245 {
246  for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
247  if (tok->varId() != varid)
248  continue;
249  if (Token::Match(tok, "%var% . release ( )"))
250  return true;
251  if (Token::Match(tok, "%var% ="))
252  return false;
253  }
254  return false;
255 }
256 
257 static bool isLocalVarNoAutoDealloc(const Token *varTok)
258 {
259  // not a local variable nor argument?
260  const Variable *var = varTok->variable();
261  if (!var)
262  return true;
263  if (!var->isArgument() && (!var->isLocal() || var->isStatic()))
264  return false;
265 
266  // Don't check reference variables
267  if (var->isReference() && !var->isArgument())
268  return false;
269 
270  // non-pod variable
271  if (varTok->isCpp()) {
272  // Possibly automatically deallocated memory
273  if (isAutoDealloc(var) && Token::Match(varTok, "%var% [=({] new"))
274  return false;
275  if (!var->isPointer() && !var->typeStartToken()->isStandardType())
276  return false;
277  }
278  return true;
279 }
280 
281 /** checks if nameToken is a name of a function in a function call:
282  * func(arg)
283  * or
284  * func<temp1_arg>(arg)
285  * @param nameToken Function name token
286  * @return opening parenthesis token or NULL if not a function call
287  */
288 
289 static const Token * isFunctionCall(const Token * nameToken)
290 {
291  if (!nameToken->isStandardType() && nameToken->isName()) {
292  nameToken = nameToken->next();
293  // check if function is a template
294  if (nameToken && nameToken->link() && nameToken->str() == "<") {
295  // skip template arguments
296  nameToken = nameToken->link()->next();
297  }
298  // check for '('
299  if (nameToken && nameToken->link() && !nameToken->isCast() && nameToken->str() == "(") {
300  // returning opening parenthesis pointer
301  return nameToken;
302  }
303  }
304  return nullptr;
305 }
306 
307 bool CheckLeakAutoVar::checkScope(const Token * const startToken,
308  VarInfo &varInfo,
309  std::set<int> notzero,
310  nonneg int recursiveCount)
311 {
312 #if ASAN
313  static const nonneg int recursiveLimit = 300;
314 #elif defined(__MINGW32__)
315  // testrunner crashes with stack overflow in CI
316  static constexpr nonneg int recursiveLimit = 600;
317 #else
318  static constexpr nonneg int recursiveLimit = 1000;
319 #endif
320  if (++recursiveCount > recursiveLimit) // maximum number of "else if ()"
321  throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT);
322 
323  std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
324  auto& possibleUsage = varInfo.possibleUsage;
325  const std::set<int> conditionalAlloc(varInfo.conditionalAlloc);
326 
327  // Parse all tokens
328  const Token * const endToken = startToken->link();
329  for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
330  if (!tok->scope()->isExecutable()) {
331  tok = tok->scope()->bodyEnd;
332  if (!tok) // Ticket #6666 (crash upon invalid code)
333  break;
334  }
335 
336  // check each token
337  {
338  const bool isInit = Token::Match(tok, "%var% {|(") && tok->variable() && tok == tok->variable()->nameToken() && tok->variable()->isPointer();
339  const Token * nextTok = isInit ? nullptr : checkTokenInsideExpression(tok, varInfo);
340  if (nextTok) {
341  tok = nextTok;
342  continue;
343  }
344  }
345 
346 
347  // look for end of statement
348  const bool isInit = Token::Match(tok->tokAt(-1), "%var% {|(") && tok->tokAt(-1)->variable() && tok->tokAt(-1) == tok->tokAt(-1)->variable()->nameToken();
349  if ((!Token::Match(tok, "[;{},]") || Token::Match(tok->next(), "[;{},]")) && !(isInit && tok->str() == "("))
350  continue;
351 
352  if (Token::Match(tok, "[;{},] %var% ["))
353  continue;
354 
355  if (!isInit)
356  tok = tok->next();
357  if (!tok || tok == endToken)
358  break;
359 
360  if (Token::Match(tok, "%name% (") && isUnevaluated(tok)) {
361  tok = tok->linkAt(1);
362  continue;
363  }
364 
365  if (Token::Match(tok, "const %type%"))
366  tok = tok->tokAt(2);
367 
368  while (!isInit && tok->str() == "(")
369  tok = tok->next();
370  while (tok->isUnaryOp("*") && tok->astOperand1()->isUnaryOp("&"))
371  tok = tok->astOperand1()->astOperand1();
372 
373  // parse statement, skip to last member
374  const Token* varTok = isInit ? tok->tokAt(-1) : tok;
375  while (Token::Match(varTok, "%name% ::|. %name% !!("))
376  varTok = varTok->tokAt(2);
377 
378  const Token *ftok = tok;
379  if (ftok->str() == "::")
380  ftok = ftok->next();
381  while (Token::Match(ftok, "%name% :: %name%"))
382  ftok = ftok->tokAt(2);
383 
384  auto isAssignment = [](const Token* varTok) -> const Token* {
385  if (varTok->varId()) {
386  const Token* top = varTok;
387  while (top->astParent()) {
388  top = top->astParent();
389  if (!Token::Match(top, "(|*|&|."))
390  break;
391  }
392  if (top->str() == "=" && succeeds(top, varTok))
393  return top;
394  }
395  return nullptr;
396  };
397 
398  // assignment..
399  if (const Token* const tokAssignOp = isInit ? varTok : isAssignment(varTok)) {
400 
401  if (Token::simpleMatch(tokAssignOp->astOperand1(), "."))
402  continue;
403  // taking address of another variable..
404  if (Token::Match(tokAssignOp, "= %var% +|;|?|%comp%")) {
405  if (varTok->tokAt(2)->varId() != varTok->varId()) {
406  // If variable points at allocated memory => error
407  leakIfAllocated(varTok, varInfo);
408 
409  // no multivariable checking currently => bail out for rhs variables
410  for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) {
411  if (tok2->str() == ";") {
412  break;
413  }
414  if (tok2->varId()) {
415  varInfo.erase(tok2->varId());
416  }
417  }
418  }
419  }
420 
421  // right ast part (after `=` operator)
422  const Token* tokRightAstOperand = tokAssignOp->astOperand2();
423  while (tokRightAstOperand && tokRightAstOperand->isCast())
424  tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
425 
426  // is variable used in rhs?
427  if (isVarUsedInTree(tokRightAstOperand, varTok->varId()))
428  continue;
429 
430  // Variable has already been allocated => error
431  if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end())
432  leakIfAllocated(varTok, varInfo);
433  varInfo.erase(varTok->varId());
434 
435  if (!isLocalVarNoAutoDealloc(varTok))
436  continue;
437 
438  // allocation?
439  const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr;
440  if (Token::Match(fTok, "%type% (")) {
442  if (f && f->arg == -1) {
443  VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
444  varAlloc.type = f->groupId;
445  varAlloc.status = VarInfo::ALLOC;
446  varAlloc.allocTok = fTok;
447  }
448 
449  changeAllocStatusIfRealloc(alloctype, fTok, varTok);
450  } else if (varTok->isCpp() && Token::Match(varTok->tokAt(2), "new !!(")) {
451  const Token* tok2 = varTok->tokAt(2)->astOperand1();
452  const bool arrayNew = (tok2 && (tok2->str() == "[" || (Token::Match(tok2, "(|{") && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
453  VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()];
454  varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
455  varAlloc.status = VarInfo::ALLOC;
456  varAlloc.allocTok = varTok->tokAt(2);
457  }
458 
459  // Assigning non-zero value variable. It might be used to
460  // track the execution for a later if condition.
461  if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toBigNumber(varTok->strAt(2)) != 0)
462  notzero.insert(varTok->varId());
463  else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName())
464  notzero.insert(varTok->varId());
465  else
466  notzero.erase(varTok->varId());
467  }
468 
469  // if/else
470  else if (Token::simpleMatch(tok, "if (")) {
471  // Parse function calls inside the condition
472 
473  const Token * closingParenthesis = tok->linkAt(1);
474  for (const Token *innerTok = tok->tokAt(2); innerTok && innerTok != closingParenthesis; innerTok = innerTok->next()) {
475  if (isUnevaluated(innerTok)) {
476  innerTok = innerTok->linkAt(1);
477  continue;
478  }
479  // TODO: replace with checkTokenInsideExpression()
480  const Token* const openingPar = isFunctionCall(innerTok);
481  if (!openingPar)
482  checkTokenInsideExpression(innerTok, varInfo);
483 
484  if (!isLocalVarNoAutoDealloc(innerTok))
485  continue;
486 
487  // Check assignments in the if-statement. Skip multiple assignments since we don't track those
488  if (Token::Match(innerTok, "%var% =") && innerTok->astParent() == innerTok->next() &&
489  !(innerTok->next()->astParent() && innerTok->next()->astParent()->isAssignmentOp())) {
490  // allocation?
491  // right ast part (after `=` operator)
492  const Token* tokRightAstOperand = innerTok->next()->astOperand2();
493  while (tokRightAstOperand && tokRightAstOperand->isCast())
494  tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1();
495  if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) {
496  const Token * fTok = tokRightAstOperand->previous();
498  if (f && f->arg == -1) {
499  VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
500  varAlloc.type = f->groupId;
501  varAlloc.status = VarInfo::ALLOC;
502  varAlloc.allocTok = fTok;
503  } else {
504  // Fixme: warn about leak
505  alloctype.erase(innerTok->varId());
506  }
507  changeAllocStatusIfRealloc(alloctype, fTok, varTok);
508  } else if (innerTok->isCpp() && Token::Match(innerTok->tokAt(2), "new !!(")) {
509  const Token* tok2 = innerTok->tokAt(2)->astOperand1();
510  const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "[")));
511  VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()];
512  varAlloc.type = arrayNew ? NEW_ARRAY : NEW;
513  varAlloc.status = VarInfo::ALLOC;
514  varAlloc.allocTok = innerTok->tokAt(2);
515  }
516  }
517 
518  // check for function call
519  if (openingPar) {
520  const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(innerTok);
521  // innerTok is a function name
522  const VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
523  functionCall(innerTok, openingPar, varInfo, allocation, allocFunc);
524  innerTok = openingPar->link();
525  }
526  }
527 
528  if (Token::simpleMatch(closingParenthesis, ") {")) {
529  VarInfo varInfo1(varInfo); // VarInfo for if code
530  VarInfo varInfo2(varInfo); // VarInfo for else code
531 
532  // Skip expressions before commas
533  const Token * astOperand2AfterCommas = tok->next()->astOperand2();
534  while (Token::simpleMatch(astOperand2AfterCommas, ","))
535  astOperand2AfterCommas = astOperand2AfterCommas->astOperand2();
536 
537  // Recursively scan variable comparisons in condition
538  visitAstNodes(astOperand2AfterCommas, [&](const Token *tok3) {
539  if (!tok3)
540  return ChildrenToVisit::none;
541  if (tok3->str() == "&&" || tok3->str() == "||") {
542  // FIXME: handle && ! || better
543  return ChildrenToVisit::op1_and_op2;
544  }
545  if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
546  return ChildrenToVisit::op2;
547  }
548  if (tok3->str() == "(" && tok3->previous()->isName()) {
549  const std::vector<const Token *> params = getArguments(tok3->previous());
550  for (const Token *par : params) {
551  if (!par->isComparisonOp())
552  continue;
553  const Token *vartok = nullptr;
554  if (isVarTokComparison(par, &vartok, alloc_success_conds) ||
555  (isVarTokComparison(par, &vartok, alloc_failed_conds))) {
556  varInfo1.erase(vartok->varId());
557  varInfo2.erase(vartok->varId());
558  }
559  }
560  return ChildrenToVisit::none;
561  }
562 
563  const Token *vartok = nullptr;
564  if (isVarTokComparison(tok3, &vartok, alloc_success_conds)) {
565  varInfo2.reallocToAlloc(vartok->varId());
566  varInfo2.erase(vartok->varId());
567  if (astIsVariableComparison(tok3, "!=", "0", &vartok) &&
568  (notzero.find(vartok->varId()) != notzero.end()))
569  varInfo2.clear();
570  } else if (isVarTokComparison(tok3, &vartok, alloc_failed_conds)) {
571  varInfo1.reallocToAlloc(vartok->varId());
572  varInfo1.erase(vartok->varId());
573  }
574  return ChildrenToVisit::none;
575  });
576 
577  if (!checkScope(closingParenthesis->next(), varInfo1, notzero, recursiveCount)) {
578  varInfo.clear();
579  continue;
580  }
581  closingParenthesis = closingParenthesis->linkAt(1);
582  if (Token::simpleMatch(closingParenthesis, "} else {")) {
583  if (!checkScope(closingParenthesis->tokAt(2), varInfo2, notzero, recursiveCount)) {
584  varInfo.clear();
585  return false;
586  }
587  tok = closingParenthesis->linkAt(2)->previous();
588  } else {
589  tok = closingParenthesis->previous();
590  }
591 
592  VarInfo old;
593  old.swap(varInfo);
594 
595  std::map<int, VarInfo::AllocInfo>::const_iterator it;
596 
597  for (it = old.alloctype.cbegin(); it != old.alloctype.cend(); ++it) {
598  const int varId = it->first;
599  if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end())
600  continue;
601  if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() ||
602  varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) {
603  varInfo1.erase(varId);
604  varInfo2.erase(varId);
605  }
606  }
607 
608  // Conditional allocation in varInfo1
609  for (it = varInfo1.alloctype.cbegin(); it != varInfo1.alloctype.cend(); ++it) {
610  if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
611  old.alloctype.find(it->first) == old.alloctype.end()) {
612  varInfo.conditionalAlloc.insert(it->first);
613  }
614  }
615 
616  // Conditional allocation in varInfo2
617  for (it = varInfo2.alloctype.cbegin(); it != varInfo2.alloctype.cend(); ++it) {
618  if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() &&
619  old.alloctype.find(it->first) == old.alloctype.end()) {
620  varInfo.conditionalAlloc.insert(it->first);
621  }
622  }
623 
624  // Conditional allocation/deallocation
625  for (it = varInfo1.alloctype.cbegin(); it != varInfo1.alloctype.cend(); ++it) {
626  if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
627  varInfo.conditionalAlloc.erase(it->first);
628  varInfo2.erase(it->first);
629  }
630  }
631  for (it = varInfo2.alloctype.cbegin(); it != varInfo2.alloctype.cend(); ++it) {
632  if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
633  varInfo.conditionalAlloc.erase(it->first);
634  varInfo1.erase(it->first);
635  }
636  }
637 
638  alloctype.insert(varInfo1.alloctype.cbegin(), varInfo1.alloctype.cend());
639  alloctype.insert(varInfo2.alloctype.cbegin(), varInfo2.alloctype.cend());
640 
641  possibleUsage.insert(varInfo1.possibleUsage.cbegin(), varInfo1.possibleUsage.cend());
642  possibleUsage.insert(varInfo2.possibleUsage.cbegin(), varInfo2.possibleUsage.cend());
643  }
644  }
645 
646  // unknown control.. (TODO: handle loops)
647  else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) {
648  varInfo.clear();
649  return false;
650  }
651 
652  // return
653  else if (tok->str() == "return") {
654  ret(tok, varInfo);
655  varInfo.clear();
656  }
657 
658  // throw
659  else if (tok->isCpp() && tok->str() == "throw") {
660  bool tryFound = false;
661  const Scope* scope = tok->scope();
662  while (scope && scope->isExecutable()) {
663  if (scope->type == Scope::eTry)
664  tryFound = true;
665  scope = scope->nestedIn;
666  }
667  // If the execution leaves the function then treat it as return
668  if (!tryFound)
669  ret(tok, varInfo);
670  varInfo.clear();
671  }
672 
673  // delete
674  else if (tok->isCpp() && tok->str() == "delete") {
675  const Token * delTok = tok;
676  if (Token::simpleMatch(delTok->astOperand1(), "."))
677  continue;
678  const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]");
679  if (arrayDelete)
680  tok = tok->tokAt(3);
681  else
682  tok = tok->next();
683  if (tok->str() == "(")
684  tok = tok->next();
685  while (Token::Match(tok, "%name% ::|."))
686  tok = tok->tokAt(2);
687  const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0;
688  if (!isnull && tok->varId() && tok->strAt(1) != "[") {
689  const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC, delTok);
690  changeAllocStatus(varInfo, allocation, tok, tok);
691  }
692  }
693 
694  // Function call..
695  else if (const Token* openingPar = isFunctionCall(ftok)) {
697  VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok);
698  if (allocation.type == 0)
699  allocation.status = VarInfo::NOALLOC;
700  if (Token::simpleMatch(ftok->astParent(), "(") && Token::simpleMatch(ftok->astParent()->astOperand2(), "."))
701  continue;
702  functionCall(ftok, openingPar, varInfo, allocation, af);
703 
704  tok = ftok->next()->link();
705 
706  // Handle scopes that might be noreturn
707  if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) {
708  if (ftok->isKeyword())
709  continue;
710  bool unknown = false;
711  if (mTokenizer->isScopeNoReturn(tok->tokAt(2), &unknown)) {
712  if (!unknown)
713  varInfo.clear();
714  else {
715  if (ftok->function() && !ftok->function()->isAttributeNoreturn() &&
716  !(ftok->function()->functionScope && mTokenizer->isScopeNoReturn(ftok->function()->functionScope->bodyEnd))) // check function scope
717  continue;
718  const std::string functionName(mSettings->library.getFunctionName(ftok));
719  if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName)) {
720  const VarInfo::Usage usage = Token::simpleMatch(openingPar, "( )") ? VarInfo::NORET : VarInfo::USED; // TODO: check parameters passed to function
721  varInfo.possibleUsageAll({ ftok, usage });
722  }
723  }
724  }
725  }
726 
727  continue;
728  }
729 
730  // goto => weird execution path
731  else if (tok->str() == "goto") {
732  varInfo.clear();
733  return false;
734  }
735 
736  // continue/break
737  else if (Token::Match(tok, "continue|break ;")) {
738  varInfo.clear();
739  }
740 
741  // Check smart pointer
742  else if (Token::Match(ftok, "%name% <") && mSettings->library.isSmartPointer(tok)) {
743  const Token * typeEndTok = ftok->linkAt(1);
744  if (!Token::Match(typeEndTok, "> %var% {|( %var% ,|)|}"))
745  continue;
746 
747  tok = typeEndTok->linkAt(2);
748 
749  const int varid = typeEndTok->next()->varId();
750  if (isPointerReleased(typeEndTok->tokAt(2), endToken, varid))
751  continue;
752 
753  bool arrayDelete = false;
754  if (Token::findsimplematch(ftok->next(), "[ ]", typeEndTok))
755  arrayDelete = true;
756 
757  // Check deleter
758  const Token * deleterToken = nullptr;
759  const Token * endDeleterToken = nullptr;
760  const Library::AllocFunc* af = nullptr;
761  if (Token::Match(ftok, "unique_ptr < %type% ,")) {
762  deleterToken = ftok->tokAt(4);
763  endDeleterToken = typeEndTok;
764  } else if (Token::Match(typeEndTok, "> %var% {|( %var% ,")) {
765  deleterToken = typeEndTok->tokAt(5);
766  endDeleterToken = typeEndTok->linkAt(2);
767  }
768  if (deleterToken) {
769  // Skip the decaying plus in expressions like +[](T*){}
770  if (deleterToken->str() == "+") {
771  deleterToken = deleterToken->next();
772  }
773  // Check if its a pointer to a function
774  const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
775  if (dtok) {
776  dtok = dtok->next();
778  }
779  if (!dtok || !af) {
780  const Token * tscopeStart = nullptr;
781  const Token * tscopeEnd = nullptr;
782  // If the deleter is a lambda, check if it calls the dealloc function
783  if (deleterToken->str() == "[" &&
784  Token::simpleMatch(deleterToken->link(), "] (") &&
785  // TODO: Check for mutable keyword
786  Token::simpleMatch(deleterToken->link()->linkAt(1), ") {")) {
787  tscopeStart = deleterToken->link()->linkAt(1)->tokAt(1);
788  tscopeEnd = tscopeStart->link();
789  // check user-defined deleter function
790  } else if (dtok && dtok->function()) {
791  const Scope* tscope = dtok->function()->functionScope;
792  if (tscope) {
793  tscopeStart = tscope->bodyStart;
794  tscopeEnd = tscope->bodyEnd;
795  }
796  // If the deleter is a class, check if class calls the dealloc function
797  } else if ((dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken)) && dtok->type()) {
798  const Scope * tscope = dtok->type()->classScope;
799  if (tscope) {
800  tscopeStart = tscope->bodyStart;
801  tscopeEnd = tscope->bodyEnd;
802  }
803  }
804 
805  if (tscopeStart && tscopeEnd) {
806  for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) {
808  if (af)
809  break;
810  }
811  } else { // there is a deleter, but we can't check it -> assume that it deallocates correctly
812  varInfo.clear();
813  continue;
814  }
815  }
816  }
817 
818  const Token * vtok = typeEndTok->tokAt(3);
819  const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, ftok);
820  changeAllocStatus(varInfo, allocation, vtok, vtok);
821  } else if (Token::Match(tok, "%var% ."))
822  checkTokenInsideExpression(tok, varInfo);
823  }
824  ret(endToken, varInfo, true);
825  return true;
826 }
827 
828 
829 const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo, bool inFuncCall)
830 {
831  // Deallocation and then dereferencing pointer..
832  if (tok->varId() > 0) {
833  // TODO : Write a separate checker for this that uses valueFlowForward.
834  const std::map<int, VarInfo::AllocInfo>::const_iterator var = varInfo.alloctype.find(tok->varId());
835  if (var != varInfo.alloctype.end()) {
836  bool unknown = false;
837  if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok, unknown, *mSettings) && !unknown) {
838  deallocUseError(tok, tok->str());
839  } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
840  varInfo.erase(tok->varId());
841  } else {
842  // check if tok is assigned into another variable
843  const Token *rhs = tok;
844  bool isAssignment = false;
845  while (rhs->astParent()) {
846  if (rhs->astParent()->str() == "=") {
847  isAssignment = true;
848  break;
849  }
850  rhs = rhs->astParent();
851  }
852  while (rhs->isCast()) {
853  rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1();
854  }
855  if (rhs->varId() == tok->varId() && isAssignment) {
856  // simple assignment
857  varInfo.erase(tok->varId());
858  } else if (rhs->astParent() && rhs->str() == "(" && !mSettings->library.returnValue(rhs->astOperand1()).empty()) {
859  // #9298, assignment through return value of a function
860  const std::string &returnValue = mSettings->library.returnValue(rhs->astOperand1());
861  if (startsWith(returnValue, "arg")) {
862  int argn;
863  const Token *func = getTokenArgumentFunction(tok, argn);
864  if (func) {
865  const std::string arg = "arg" + std::to_string(argn + 1);
866  if (returnValue == arg) {
867  varInfo.erase(tok->varId());
868  }
869  }
870  }
871  }
872  }
873  } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) {
874  varInfo.referenced.insert(tok->tokAt(2)->varId());
875  }
876  }
877 
878  // check for function call
879  const Token * const openingPar = inFuncCall ? nullptr : isFunctionCall(tok);
880  if (openingPar) {
881  const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok);
882  VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok);
883  if (alloc.type == 0)
884  alloc.status = VarInfo::NOALLOC;
885  functionCall(tok, openingPar, varInfo, alloc, nullptr);
886  const std::string &returnValue = mSettings->library.returnValue(tok);
887  if (startsWith(returnValue, "arg"))
888  // the function returns one of its argument, we need to process a potential assignment
889  return openingPar;
890  return isCPPCast(tok->astParent()) ? openingPar : openingPar->link();
891  }
892 
893  return nullptr;
894 }
895 
896 
897 void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map<int, VarInfo::AllocInfo> &alloctype, const Token *fTok, const Token *retTok) const
898 {
900  if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) {
901  const Token* argTok = getArguments(fTok).at(f->reallocArg - 1);
902  if (alloctype.find(argTok->varId()) != alloctype.end()) {
903  VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()];
904  if (argAlloc.type != 0 && argAlloc.type != f->groupId)
905  mismatchError(fTok, argAlloc.allocTok, argTok->str());
906  argAlloc.status = VarInfo::REALLOC;
907  argAlloc.allocTok = fTok;
908  }
909  VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()];
910  retAlloc.type = f->groupId;
911  retAlloc.status = VarInfo::ALLOC;
912  retAlloc.allocTok = fTok;
913  retAlloc.reallocedFromType = argTok->varId();
914  }
915 }
916 
917 
918 void CheckLeakAutoVar::changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
919 {
920  std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
921  const std::map<int, VarInfo::AllocInfo>::iterator var = alloctype.find(arg->varId());
922  if (var != alloctype.end()) {
923  if (allocation.status == VarInfo::NOALLOC) {
924  // possible usage
925  varInfo.possibleUsage[arg->varId()] = { tok, VarInfo::USED };
926  if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
927  varInfo.erase(arg->varId());
928  } else if (var->second.managed()) {
929  doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type);
930  var->second.status = allocation.status;
931  } else if (var->second.type != allocation.type && var->second.type != 0) {
932  // mismatching allocation and deallocation
933  mismatchError(tok, var->second.allocTok, arg->str());
934  varInfo.erase(arg->varId());
935  } else {
936  // deallocation
937  var->second.status = allocation.status;
938  var->second.type = allocation.type;
939  var->second.allocTok = allocation.allocTok;
940  }
941  } else if (allocation.status != VarInfo::NOALLOC && allocation.status != VarInfo::OWNED && !Token::simpleMatch(tok->astTop(), "return")) {
942  alloctype[arg->varId()].status = VarInfo::DEALLOC;
943  alloctype[arg->varId()].allocTok = tok;
944  }
945 }
946 
947 void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af)
948 {
949  // Ignore function call?
950  const bool isLeakIgnore = mSettings->library.isLeakIgnore(mSettings->library.getFunctionName(tokName));
951  if (mSettings->library.getReallocFuncInfo(tokName))
952  return;
953  if (tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike)
954  return;
955 
956  const Token * const tokFirstArg = tokOpeningPar->next();
957  if (!tokFirstArg || tokFirstArg->str() == ")") {
958  // no arguments
959  return;
960  }
961 
962  int argNr = 1;
963  for (const Token *funcArg = tokFirstArg; funcArg; funcArg = funcArg->nextArgument()) {
964  const Token* arg = funcArg;
965  if (arg->isCpp()) {
966  int tokAdvance = 0;
967  if (arg->str() == "new")
968  tokAdvance = 1;
969  else if (Token::simpleMatch(arg, "* new"))
970  tokAdvance = 2;
971  if (tokAdvance > 0) {
972  arg = arg->tokAt(tokAdvance);
973  if (Token::simpleMatch(arg, "( std :: nothrow )"))
974  arg = arg->tokAt(5);
975  }
976  }
977 
978  // Skip casts
979  if (arg->isKeyword() && arg->astParent() && arg->astParent()->isCast())
980  arg = arg->astParent();
981  while (arg && arg->isCast())
982  arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1();
983  const Token * const argTypeStartTok = arg;
984 
985  while (Token::Match(arg, "%name% .|:: %name%"))
986  arg = arg->tokAt(2);
987 
988  if ((Token::Match(arg, "%var% [-,)] !!.") && !(arg->variable() && arg->variable()->isArray())) ||
989  (Token::Match(arg, "& %var% !!.") && !(arg->next()->variable() && arg->next()->variable()->isArray()))) {
990  // goto variable
991  const bool isAddressOf = arg->str() == "&";
992  if (isAddressOf)
993  arg = arg->next();
994 
995  const bool isnull = !isAddressOf && (arg->hasKnownIntValue() && arg->values().front().intvalue == 0);
996 
997  // Is variable allocated?
998  if (!isnull && (!af || af->arg == argNr)) {
999  const Library::AllocFunc* deallocFunc = mSettings->library.getDeallocFuncInfo(tokName);
1000  VarInfo::AllocInfo dealloc(deallocFunc ? deallocFunc->groupId : 0, VarInfo::DEALLOC, tokName);
1001  if (const Library::AllocFunc* allocFunc = mSettings->library.getAllocFuncInfo(tokName)) {
1002  if (mSettings->library.getDeallocFuncInfo(tokName)) {
1003  changeAllocStatus(varInfo, dealloc.type == 0 ? allocation : dealloc, tokName, arg);
1004  }
1005  if (allocFunc->arg == argNr &&
1006  !(arg->variable() && arg->variable()->isArgument() && arg->valueType() && arg->valueType()->pointer > 1) &&
1007  (isAddressOf || (arg->valueType() && arg->valueType()->pointer == 2))) {
1008  leakIfAllocated(arg, varInfo);
1009  VarInfo::AllocInfo& varAlloc = varInfo.alloctype[arg->varId()];
1010  varAlloc.type = allocFunc->groupId;
1011  varAlloc.status = VarInfo::ALLOC;
1012  varAlloc.allocTok = arg;
1013  }
1014  }
1015  else if (isLeakIgnore)
1016  checkTokenInsideExpression(arg, varInfo);
1017  else
1018  changeAllocStatus(varInfo, dealloc.type == 0 ? allocation : dealloc, tokName, arg);
1019  }
1020  }
1021  // Check smart pointer
1022  else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) {
1023  const Token * typeEndTok = arg->linkAt(1);
1024  const Token * allocTok = nullptr;
1025  if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}"))
1026  continue;
1027 
1028  bool arrayDelete = false;
1029  if (Token::findsimplematch(arg->next(), "[ ]", typeEndTok))
1030  arrayDelete = true;
1031 
1032  // Check deleter
1033  const Token * deleterToken = nullptr;
1034  const Token * endDeleterToken = nullptr;
1035  const Library::AllocFunc* sp_af = nullptr;
1036  if (Token::Match(arg, "unique_ptr < %type% ,")) {
1037  deleterToken = arg->tokAt(4);
1038  endDeleterToken = typeEndTok;
1039  } else if (Token::Match(typeEndTok, "> {|( %var% ,")) {
1040  deleterToken = typeEndTok->tokAt(4);
1041  endDeleterToken = typeEndTok->linkAt(1);
1042  }
1043  if (deleterToken) {
1044  // Check if its a pointer to a function
1045  const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken);
1046  if (dtok) {
1047  sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1));
1048  } else {
1049  // If the deleter is a class, check if class calls the dealloc function
1050  dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken);
1051  if (dtok && dtok->type()) {
1052  const Scope * tscope = dtok->type()->classScope;
1053  for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) {
1054  sp_af = mSettings->library.getDeallocFuncInfo(tok2);
1055  if (sp_af) {
1056  allocTok = tok2;
1057  break;
1058  }
1059  }
1060  }
1061  }
1062  }
1063 
1064  const Token * vtok = typeEndTok->tokAt(2);
1065  const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, allocTok);
1066  changeAllocStatus(varInfo, sp_allocation, vtok, vtok);
1067  } else {
1068  const Token* const nextArg = funcArg->nextArgument();
1069  while (arg && ((nextArg && arg != nextArg) || (!nextArg && arg != tokOpeningPar->link()))) {
1070  checkTokenInsideExpression(arg, varInfo, /*inFuncCall*/ isLeakIgnore);
1071 
1072  if (isLambdaCaptureList(arg))
1073  break;
1074  arg = arg->next();
1075  }
1076  }
1077  // TODO: check each token in argument expression (could contain multiple variables)
1078  argNr++;
1079  }
1080 }
1081 
1082 
1084  const VarInfo &varInfo)
1085 {
1086  const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
1087  const auto& possibleUsage = varInfo.possibleUsage;
1088 
1089  const std::map<int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
1090  if (var != alloctype.cend() && var->second.status == VarInfo::ALLOC) {
1091  const auto use = possibleUsage.find(vartok->varId());
1092  if (use == possibleUsage.end()) {
1093  leakError(vartok, vartok->str(), var->second.type);
1094  } else {
1095  configurationInfo(vartok, use->second);
1096  }
1097  }
1098 }
1099 
1100 static const Token* getOutparamAllocation(const Token* tok, const Settings& settings)
1101 {
1102  if (!tok)
1103  return nullptr;
1104  int argn{};
1105  const Token* ftok = getTokenArgumentFunction(tok, argn);
1106  if (!ftok)
1107  return nullptr;
1108  if (const Library::AllocFunc* allocFunc = settings.library.getAllocFuncInfo(ftok)) {
1109  if (allocFunc->arg == argn + 1)
1110  return ftok;
1111  }
1112  return nullptr;
1113 }
1114 
1115 void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope)
1116 {
1117  const std::map<int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
1118  const auto& possibleUsage = varInfo.possibleUsage;
1119  std::vector<int> toRemove;
1120 
1121  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1122  for (std::map<int, VarInfo::AllocInfo>::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) {
1123  // don't warn if variable is conditionally allocated, unless it leaves the scope
1124  if (!isEndOfScope && !it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
1125  continue;
1126 
1127  // don't warn if there is a reference of the variable
1128  if (varInfo.referenced.find(it->first) != varInfo.referenced.end())
1129  continue;
1130 
1131  const int varid = it->first;
1132  const Variable *var = symbolDatabase->getVariableFromVarId(varid);
1133  if (var) {
1134  // don't warn if we leave an inner scope
1135  if (isEndOfScope && var->scope() && tok != var->scope()->bodyEnd)
1136  continue;
1137  enum class PtrUsage { NONE, DEREF, PTR } used = PtrUsage::NONE;
1138  for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
1139  if (tok2->str() == ";")
1140  break;
1141  if (!Token::Match(tok2, "return|(|{|,|*"))
1142  continue;
1143 
1144  const Token* tok3 = tok2->next();
1145  while (tok3 && tok3->isCast() && tok3->valueType() &&
1146  (tok3->valueType()->pointer ||
1147  (tok3->valueType()->typeSize(mSettings->platform) == 0) ||
1149  tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1();
1150  if (tok3 && tok3->varId() == varid)
1151  tok2 = tok3->next();
1152  else if (Token::Match(tok3, "& %varid% . %name%", varid))
1153  tok2 = tok3->tokAt(4);
1154  else if (Token::simpleMatch(tok3, "*") && tok3->next()->varId() == varid)
1155  tok2 = tok3;
1156  else
1157  continue;
1158  if (Token::Match(tok2, "[});,+]") && (!astIsBool(tok) || tok2->str() != ";")) {
1159  used = PtrUsage::PTR;
1160  break;
1161  }
1162  if (Token::Match(tok2, "[|.|*")) {
1163  used = PtrUsage::DEREF;
1164  break;
1165  }
1166  }
1167 
1168  // don't warn when returning after checking return value of outparam allocation
1169  const Token* outparamFunc{};
1170  if ((tok->scope()->type == Scope::ScopeType::eIf || tok->scope()->type== Scope::ScopeType::eElse) &&
1171  (outparamFunc = getOutparamAllocation(it->second.allocTok, *mSettings))) {
1172  const Scope* scope = tok->scope();
1173  if (scope->type == Scope::ScopeType::eElse) {
1174  scope = scope->bodyStart->tokAt(-2)->scope();
1175  }
1176  const Token* const ifEnd = scope->bodyStart->previous();
1177  const Token* const ifStart = ifEnd->link();
1178  const Token* const alloc = it->second.allocTok;
1179  if (precedes(ifStart, alloc) && succeeds(ifEnd, alloc)) { // allocation and check in if
1180  if (Token::Match(outparamFunc->next()->astParent(), "%comp%"))
1181  continue;
1182  } else { // allocation result assigned to variable
1183  const Token* const retAssign = outparamFunc->next()->astParent();
1184  if (Token::simpleMatch(retAssign, "=") && retAssign->astOperand1()->varId()) {
1185  bool isRetComp = false;
1186  for (const Token* tok2 = ifStart; tok2 != ifEnd; tok2 = tok2->next()) {
1187  if (tok2->varId() == retAssign->astOperand1()->varId()) {
1188  isRetComp = true;
1189  break;
1190  }
1191  }
1192  if (isRetComp)
1193  continue;
1194  }
1195  }
1196  }
1197 
1198  // return deallocated pointer
1199  if (used != PtrUsage::NONE && it->second.status == VarInfo::DEALLOC)
1200  deallocReturnError(tok, it->second.allocTok, var->name());
1201 
1202  else if (used != PtrUsage::PTR && !it->second.managed() && !var->isReference()) {
1203  const auto use = possibleUsage.find(varid);
1204  if (use == possibleUsage.end()) {
1205  leakError(tok, var->name(), it->second.type);
1206  } else if (!use->second.first->variable()) { // TODO: handle constructors
1207  configurationInfo(tok, use->second);
1208  }
1209  }
1210  toRemove.push_back(varid);
1211  }
1212  }
1213  for (const int varId : toRemove)
1214  varInfo.erase(varId);
1215 }
const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok)
Is given syntax tree a variable comparison against value.
Definition: astutils.cpp:351
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:3083
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:994
const Token * getTokenArgumentFunction(const Token *tok, int &argn)
Return the token to the function and the argument number.
Definition: astutils.cpp:2360
bool astIsBool(const Token *tok)
Is expression of boolean type?
Definition: astutils.cpp:215
bool isCPPCast(const Token *tok)
Definition: astutils.cpp:3247
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3612
bool succeeds(const Token *tok1, const Token *tok2)
If tok1 comes after tok2.
Definition: astutils.cpp:1006
int numberOfArguments(const Token *ftok)
Determines the number of arguments - if token is a function call or macro.
Definition: astutils.cpp:3063
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
static bool isVarUsedInTree(const Token *tok, nonneg int varid)
static bool isAutoDeallocType(const Type *type)
static bool isVarTokComparison(const Token *tok, const Token **vartok, const std::array< std::pair< std::string, std::string >, N > &ops)
static bool isAutoDealloc(const Variable *var)
Is variable type some class with automatic deallocation?
static const Token * isFunctionCall(const Token *nameToken)
checks if nameToken is a name of a function in a function call: func(arg) or func<temp1_arg>(arg)
static const CWE CWE415(415U)
static constexpr int NEW
static const std::array< std::pair< std::string, std::string >, 4 > alloc_failed_conds
static const std::array< std::pair< std::string, std::string >, 4 > alloc_success_conds
static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid)
static bool isLocalVarNoAutoDealloc(const Token *varTok)
static const Token * getOutparamAllocation(const Token *tok, const Settings &settings)
static constexpr int NEW_ARRAY
static const CWE CWE672(672U)
Check for memory leaks
Check for leaks.
void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope=false)
return.
void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo &allocation, const Library::AllocFunc *af)
parse function call
bool checkScope(const Token *const startToken, VarInfo &varInfo, std::set< int > notzero, nonneg int recursiveCount)
check for leaks in a function scope
void configurationInfo(const Token *tok, const std::pair< const Token *, VarInfo::Usage > &functionUsage)
message: user configuration is needed to complete analysis
void leakError(const Token *tok, const std::string &varname, int type) const
void deallocUseError(const Token *tok, const std::string &varname) const
const Token * checkTokenInsideExpression(const Token *const tok, VarInfo &varInfo, bool inFuncCall=false)
Check token inside expression.
void leakIfAllocated(const Token *vartok, const VarInfo &varInfo)
if variable is allocated then there is a leak
void changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo &allocation, const Token *tok, const Token *arg)
parse changes in allocation status
void check()
check for leaks in all scopes
void changeAllocStatusIfRealloc(std::map< int, VarInfo::AllocInfo > &alloctype, const Token *fTok, const Token *retTok) const
update allocation status if reallocation function
void mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const
void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type)
void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname)
Base class for memory leaks checking.
void deallocuseError(const Token *tok, const std::string &varname) const
void resourceLeakError(const Token *tok, const std::string &varname) const
Report that there is a resource leak (fopen/popen/etc)
void mismatchAllocDealloc(const std::list< const Token * > &callstack, const std::string &varname) const
void memleakError(const Token *tok, const std::string &varname) const
Report that there is a memory leak (new/malloc/etc)
bool isPointerDeRef(const Token *tok, bool &unknown) const
Is there a pointer dereference? Everything that should result in a nullpointer dereference error mess...
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
ErrorLogger *const mErrorLogger
Definition: check.h:135
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
static bool isresource(const int id)
is allocation type resource?
Definition: library.h:148
bool isSmartPointer(const Token *tok) const
Definition: library.cpp:1713
const std::string & returnValue(const Token *ftok) const
Definition: library.cpp:1436
bool isLeakIgnore(const std::string &functionName) const
Definition: library.cpp:1527
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
const AllocFunc * getReallocFuncInfo(const Token *tok) const
get reallocation info for function
Definition: library.cpp:1095
std::string getFunctionName(const Token *ftok) const
Get function name for function call.
Definition: library.cpp:1016
const AllocFunc * getDeallocFuncInfo(const Token *tok) const
get deallocation info for function
Definition: library.cpp:1086
bool isUse(const std::string &functionName) const
Definition: library.cpp:1520
static bigint toBigNumber(const std::string &str)
for conversion of numeric literals - for atoi-like conversions please use strToInt()
Definition: mathlib.cpp:368
std::size_t sizeof_pointer
Definition: platform.h:102
bool hasInlineOrLambdaFunction() const
std::list< Variable > varlist
ScopeType type
const Scope * nestedIn
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
nonneg int numConstructors
bool isExecutable() const
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
bool checkLibrary
Check for incomplete info in library files?
Definition: settings.h:135
Library library
Library.
Definition: settings.h:237
bool clang
Use Clang.
Definition: settings.h:150
Platform platform
Definition: settings.h:255
const Variable * getVariableFromVarId(nonneg int varId) const
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
Token * astTop()
Definition: token.h:1416
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:688
bool isKeyword() const
Definition: token.h:358
bool isName() const
Definition: token.h:361
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1065
bool hasKnownIntValue() const
Definition: token.cpp:2519
bool isUpperCaseName() const
Definition: token.cpp:203
bool isCpp() const
Definition: token.cpp:2718
const ValueType * valueType() const
Definition: token.h:331
const std::string & strAt(int index) const
Definition: token.cpp:423
void astOperand1(Token *tok)
Definition: token.cpp:1456
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1093
nonneg int varId() const
Definition: token.h:870
bool isCast() const
Definition: token.h:458
static const Token * findsimplematch(const Token *const startTok, const char(&pattern)[count])
Definition: token.h:763
const Token * tokAt(int index) const
Definition: token.cpp:393
void astOperand2(Token *tok)
Definition: token.cpp:1468
void scope(const Scope *s)
Associate this token with given scope.
Definition: token.h:1042
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:413
Token * previous()
Definition: token.h:862
void type(const ::Type *t)
Associate this token with given type.
Definition: token.cpp:2333
bool isStandardType() const
Definition: token.h:449
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
Token * next()
Definition: token.h:830
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1197
const Token * nextArgument() const
Definition: token.cpp:869
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1437
bool isScopeNoReturn(const Token *endScopeToken, bool *unknown=nullptr) const
Check if inner scope ends with a call to a noreturn function.
Definition: tokenize.cpp:8021
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
Information about a class type.
std::vector< BaseInfo > derivedFrom
const Scope * classScope
enum ValueType::Type type
const Library::Container * container
If the type is a container defined in a cfg file, this is the used.
MathLib::bigint typeSize(const Platform &platform, bool p=false) const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
void possibleUsageAll(const std::pair< const Token *, Usage > &functionUsage)
set possible usage for all variables
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isReference() const
Is reference variable.
bool isLocal() const
Is variable local.
const Type * type() const
Get Type pointer of known type.
const Scope * scope() const
Get Scope pointer of enclosing scope.
const std::string & name() const
Get name string.
const Token * typeStartToken() const
Get type start token.
bool isPointer() const
Is pointer variable.
bool isStatic() const
Is variable static.
const ValueType * valueType() const
#define nonneg
Definition: config.h:138
@ information
Checking information.
@ error
Programming error.
Simple container to be thrown when internal error is detected.
Definition: errortypes.h:36
int type
Allocation type.
const Token * allocTok
const Token * isLambdaCaptureList(const Token *tok)
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
Definition: utils.h:94