Cppcheck
checkautovariables.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 // Auto variables checks
21 //---------------------------------------------------------------------------
22 
23 #include "checkautovariables.h"
24 
25 #include "astutils.h"
26 #include "library.h"
27 #include "settings.h"
28 #include "symboldatabase.h"
29 #include "token.h"
30 #include "tokenize.h"
31 #include "valueflow.h"
32 #include "vfvalue.h"
33 
34 #include <algorithm>
35 #include <list>
36 #include <unordered_set>
37 #include <utility>
38 #include <vector>
39 
40 //---------------------------------------------------------------------------
41 
42 
43 // Register this check class into cppcheck by creating a static instance of it..
44 namespace {
45  CheckAutoVariables instance;
46 }
47 
48 static const CWE CWE398(398U); // Indicator of Poor Code Quality
49 static const CWE CWE562(562U); // Return of Stack Variable Address
50 static const CWE CWE590(590U); // Free of Memory not on the Heap
51 
52 static bool isPtrArg(const Token *tok)
53 {
54  const Variable *var = tok->variable();
55  return (var && var->isArgument() && var->isPointer());
56 }
57 
58 static bool isArrayArg(const Token *tok, const Settings& settings)
59 {
60  const Variable *var = tok->variable();
61  return (var && var->isArgument() && var->isArray() && !settings.library.isentrypoint(var->scope()->className));
62 }
63 
64 static bool isArrayVar(const Token *tok)
65 {
66  const Variable *var = tok->variable();
67  return (var && var->isArray() && !var->isArgument());
68 }
69 
70 static bool isRefPtrArg(const Token *tok)
71 {
72  const Variable *var = tok->variable();
73  return (var && var->isArgument() && var->isReference() && var->isPointer());
74 }
75 
76 static bool isNonReferenceArg(const Token *tok)
77 {
78  const Variable *var = tok->variable();
79  return (var && var->isArgument() && !var->isReference() && (var->isPointer() || (var->valueType() && var->valueType()->type >= ValueType::Type::CONTAINER) || var->type()));
80 }
81 
82 static bool isAutoVar(const Token *tok)
83 {
84  const Variable *var = tok->variable();
85 
86  if (!var || !var->isLocal() || var->isStatic())
87  return false;
88 
89  if (var->isReference()) {
90  // address of reference variable can be taken if the address
91  // of the variable it points at is not a auto-var
92  // TODO: check what the reference variable references.
93  return false;
94  }
95 
96  if (Token::Match(tok, "%name% .|::")) {
97  do {
98  tok = tok->tokAt(2);
99  } while (Token::Match(tok, "%name% .|::"));
100  if (Token::Match(tok, "%name% ("))
101  return false;
102  }
103  return true;
104 }
105 
106 static bool isAutoVarArray(const Token *tok)
107 {
108  if (!tok)
109  return false;
110 
111  // &x[..]
112  if (tok->isUnaryOp("&") && Token::simpleMatch(tok->astOperand1(), "["))
113  return isAutoVarArray(tok->astOperand1()->astOperand1());
114 
115  // x+y
116  if (tok->str() == "+")
117  return isAutoVarArray(tok->astOperand1()) || isAutoVarArray(tok->astOperand2());
118 
119  // x-intexpr
120  if (tok->str() == "-")
121  return isAutoVarArray(tok->astOperand1()) &&
122  tok->astOperand2() &&
123  tok->astOperand2()->valueType() &&
124  tok->astOperand2()->valueType()->isIntegral();
125 
126  const Variable *var = tok->variable();
127  if (!var)
128  return false;
129 
130  // Variable
131  if (var->isLocal() && !var->isStatic() && var->isArray() && !var->isPointer())
132  return true;
133 
134  // ValueFlow
135  if (var->isPointer() && !var->isArgument()) {
136  for (std::list<ValueFlow::Value>::const_iterator it = tok->values().cbegin(); it != tok->values().cend(); ++it) {
137  const ValueFlow::Value &val = *it;
138  if (val.isTokValue() && isAutoVarArray(val.tokvalue))
139  return true;
140  }
141  }
142 
143  return false;
144 }
145 
146 static bool isLocalContainerBuffer(const Token* tok)
147 {
148  if (!tok)
149  return false;
150 
151  // x+y
152  if (tok->str() == "+")
154 
155  if (tok->str() != "(" || !Token::simpleMatch(tok->astOperand1(), "."))
156  return false;
157 
158  tok = tok->astOperand1()->astOperand1();
159 
160  const Variable* var = tok->variable();
161  if (!var || !var->isLocal() || var->isStatic())
162  return false;
163 
164  const Library::Container::Yield yield = astContainerYield(tok);
165 
167 }
168 
169 static bool isAddressOfLocalVariable(const Token *expr)
170 {
171  if (!expr)
172  return false;
173  if (Token::Match(expr, "+|-"))
175  if (expr->isCast())
176  return isAddressOfLocalVariable(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1());
177  if (expr->isUnaryOp("&")) {
178  const Token *op = expr->astOperand1();
179  bool deref = false;
180  while (Token::Match(op, ".|[")) {
181  if (op->originalName() == "->")
182  return false;
183  if (op->str() == "[")
184  deref = true;
185  op = op->astOperand1();
186  }
187  return op && isAutoVar(op) && (!deref || !op->variable()->isPointer());
188  }
189  return false;
190 }
191 
192 static bool variableIsUsedInScope(const Token* start, nonneg int varId, const Scope *scope)
193 {
194  if (!start) // Ticket #5024
195  return false;
196 
197  for (const Token *tok = start; tok && tok != scope->bodyEnd; tok = tok->next()) {
198  if (tok->varId() == varId)
199  return true;
200  const Scope::ScopeType scopeType = tok->scope()->type;
201  if (scopeType == Scope::eFor || scopeType == Scope::eDo || scopeType == Scope::eWhile) // In case of loops, better checking would be necessary
202  return true;
203  if (Token::simpleMatch(tok, "asm ("))
204  return true;
205  }
206  return false;
207 }
208 
210 {
211  const bool printStyle = mSettings->severity.isEnabled(Severity::style);
212  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
213  if (!printStyle && !printWarning && !mSettings->isPremiumEnabled("uselessAssignmentPtrArg"))
214  return;
215 
216  logChecker("CheckAutoVariables::assignFunctionArg"); // style,warning
217 
218  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
219  for (const Scope * scope : symbolDatabase->functionScopes) {
220  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
221  // TODO: What happens if this is removed?
222  if (tok->astParent())
223  continue;
224  if (!(tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) || !Token::Match(tok->astOperand1(), "%var%"))
225  continue;
226  const Token* const vartok = tok->astOperand1();
227  if (isNonReferenceArg(vartok) &&
228  !Token::Match(vartok->next(), "= %varid% ;", vartok->varId()) &&
229  !variableIsUsedInScope(Token::findsimplematch(vartok->next(), ";"), vartok->varId(), scope) &&
230  !Token::findsimplematch(vartok, "goto", scope->bodyEnd)) {
231  if (vartok->variable()->isPointer() && printWarning)
233  else if (printStyle)
235  }
236  }
237  }
238 }
239 
240 static bool isAutoVariableRHS(const Token* tok) {
242 }
243 
244 static bool hasOverloadedAssignment(const Token* tok, bool& inconclusive)
245 {
246  inconclusive = false;
247  if (tok->isC())
248  return false;
249  if (const ValueType* vt = tok->valueType()) {
250  if (vt->pointer && !Token::simpleMatch(tok->astParent(), "*"))
251  return false;
252  if (vt->container && vt->container->stdStringLike)
253  return true;
254  if (vt->typeScope)
255  return std::any_of(vt->typeScope->functionList.begin(), vt->typeScope->functionList.end(), [](const Function& f) { // TODO: compare argument type
256  return f.name() == "operator=";
257  });
258  return false;
259  }
260  inconclusive = true;
261  return true;
262 }
263 
265 {
266  logChecker("CheckAutoVariables::autoVariables");
267 
268  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
269  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
270  for (const Scope * scope : symbolDatabase->functionScopes) {
271  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
272  // Skip lambda..
273  if (const Token *lambdaEndToken = findLambdaEndToken(tok)) {
274  tok = lambdaEndToken;
275  continue;
276  }
277  // Critical assignment
278  if (Token::Match(tok, "[;{}] %var% =") && isRefPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(2)->astOperand2())) {
279  checkAutoVariableAssignment(tok->next(), false);
280  } else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAutoVariableRHS(tok->tokAt(3)->astOperand2())) {
281  const Token* lhs = tok->tokAt(2);
282  bool inconclusive = false;
283  if (!hasOverloadedAssignment(lhs, inconclusive) || (printInconclusive && inconclusive))
285  tok = tok->tokAt(4);
286  } else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(4)->astOperand2())) {
287  const Token* lhs = tok->tokAt(3);
288  bool inconclusive = false;
289  if (!hasOverloadedAssignment(lhs, inconclusive) || (printInconclusive && inconclusive))
291  tok = tok->tokAt(5);
292  } else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") &&
293  (isPtrArg(tok->next()) || isArrayArg(tok->next(), *mSettings)) &&
294  isAutoVariableRHS(tok->linkAt(2)->next()->astOperand2())) {
295  errorAutoVariableAssignment(tok->next(), false);
296  }
297  // Invalid pointer deallocation
298  else if ((Token::Match(tok, "%name% ( %var%|%str% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) ||
299  (tok->isCpp() && Token::Match(tok, "delete [| ]| (| %var%|%str% !!["))) {
300  tok = Token::findmatch(tok->next(), "%var%|%str%");
301  if (Token::simpleMatch(tok->astParent(), "."))
302  continue;
303  if (isArrayVar(tok) || tok->tokType() == Token::eString)
304  errorInvalidDeallocation(tok, nullptr);
305  else if (tok->variable() && tok->variable()->isPointer()) {
306  for (const ValueFlow::Value &v : tok->values()) {
307  if (v.isImpossible())
308  continue;
309  if ((v.isTokValue() && (isArrayVar(v.tokvalue) || ((v.tokvalue->tokType() == Token::eString)))) ||
310  (v.isLocalLifetimeValue() && v.lifetimeKind == ValueFlow::Value::LifetimeKind::Address && !Token::simpleMatch(v.tokvalue, "("))) {
311  errorInvalidDeallocation(tok, &v);
312  break;
313  }
314  }
315  }
316  } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) ||
317  (tok->isCpp() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) {
318  tok = Token::findmatch(tok->next(), "%var%");
319  if (isAutoVar(tok))
320  errorInvalidDeallocation(tok, nullptr);
321  }
322  }
323  }
324 }
325 
326 bool CheckAutoVariables::checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken)
327 {
328  if (!startToken)
329  startToken = Token::findsimplematch(expr, "=")->next();
330  for (const Token *tok = startToken; tok; tok = tok->next()) {
331  if (tok->str() == "}" && tok->scope()->type == Scope::ScopeType::eFunction)
333 
334  if (Token::Match(tok, "return|throw|break|continue")) {
336  return true;
337  }
338  if (Token::simpleMatch(tok, "=")) {
339  const Token *lhs = tok;
340  while (Token::Match(lhs->previous(), "%name%|.|*|]")) {
341  if (lhs->linkAt(-1))
342  lhs = lhs->linkAt(-1);
343  else
344  lhs = lhs->previous();
345  }
346  const Token *e = expr;
347  while (e->str() != "=" && lhs->str() == e->str()) {
348  e = e->next();
349  lhs = lhs->next();
350  }
351  if (lhs->str() == "=")
352  return false;
353  }
354 
355  if (Token::simpleMatch(tok, "if (")) {
356  const Token *ifStart = tok->linkAt(1)->next();
357  return checkAutoVariableAssignment(expr, inconclusive, ifStart) || checkAutoVariableAssignment(expr, inconclusive, ifStart->link()->next());
358  }
359  if (Token::simpleMatch(tok, "} else {"))
360  tok = tok->linkAt(2);
361  }
362  return false;
363 }
364 
365 //---------------------------------------------------------------------------
366 
367 void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive)
368 {
369  if (!inconclusive) {
370  reportError(tok, Severity::error, "autoVariables",
371  "Address of local auto-variable assigned to a function parameter.\n"
372  "Dangerous assignment - the function parameter is assigned the address of a local "
373  "auto-variable. Local auto-variables are reserved from the stack which "
374  "is freed when the function ends. So the pointer to a local variable "
375  "is invalid after the function ends.", CWE562, Certainty::normal);
376  } else {
377  reportError(tok, Severity::error, "autoVariables",
378  "Address of local auto-variable assigned to a function parameter.\n"
379  "Function parameter is assigned the address of a local auto-variable. "
380  "Local auto-variables are reserved from the stack which is freed when "
381  "the function ends. The address is invalid after the function ends and it "
382  "might 'leak' from the function through the parameter.",
383  CWE562,
385  }
386 }
387 
389 {
390  reportError(tok,
392  "uselessAssignmentArg",
393  "Assignment of function parameter has no effect outside the function.", CWE398, Certainty::normal);
394 }
395 
397 {
398  reportError(tok,
400  "uselessAssignmentPtrArg",
401  "Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?", CWE398, Certainty::normal);
402 }
403 
404 bool CheckAutoVariables::diag(const Token* tokvalue)
405 {
406  if (!tokvalue)
407  return true;
408  return !mDiagDanglingTemp.insert(tokvalue).second;
409 }
410 
411 //---------------------------------------------------------------------------
412 
413 static bool isInScope(const Token * tok, const Scope * scope)
414 {
415  if (!tok)
416  return false;
417  if (!scope)
418  return false;
419  const Variable * var = tok->variable();
420  if (var && (var->isGlobal() || var->isStatic() || var->isExtern()))
421  return false;
422  if (tok->scope() && !tok->scope()->isClassOrStructOrUnion() && tok->scope()->isNestedIn(scope))
423  return true;
424  if (!var)
425  return false;
426  if (var->isArgument() && !var->isReference()) {
427  const Scope * tokScope = tok->scope();
428  if (!tokScope)
429  return false;
430  if (std::any_of(tokScope->nestedList.cbegin(), tokScope->nestedList.cend(), [&](const Scope* argScope) {
431  return argScope && argScope->isNestedIn(scope);
432  }))
433  return true;
434  }
435  return false;
436 }
437 
438 static bool isDeadScope(const Token * tok, const Scope * scope)
439 {
440  if (!tok)
441  return false;
442  if (!scope)
443  return false;
444  const Variable * var = tok->variable();
445  if (var && (!var->isLocal() || var->isStatic() || var->isExtern()))
446  return false;
447  if (tok->scope() && tok->scope()->bodyEnd != scope->bodyEnd && precedes(tok->scope()->bodyEnd, scope->bodyEnd))
448  return true;
449  return false;
450 }
451 
452 static int getPointerDepth(const Token *tok)
453 {
454  if (!tok)
455  return 0;
456  if (tok->valueType())
457  return tok->valueType()->pointer;
458  int n = 0;
459  std::pair<const Token*, const Token*> decl = Token::typeDecl(tok);
460  for (const Token* tok2 = decl.first; tok2 != decl.second; tok2 = tok2->next())
461  if (Token::simpleMatch(tok2, "*"))
462  n++;
463  return n;
464 }
465 
466 static bool isDeadTemporary(const Token* tok, const Token* expr, const Library* library)
467 {
468  if (!isTemporary(tok, library))
469  return false;
470  if (expr) {
472  return false;
473  const Token* parent = tok->astParent();
474  if (Token::simpleMatch(parent, "{") && Token::simpleMatch(parent->astParent(), ":"))
475  parent = parent->astParent();
476  // Is in a for loop
477  if (astIsRHS(tok) && Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "(") && Token::simpleMatch(parent->astParent()->previous(), "for (")) {
478  const Token* braces = parent->astParent()->link()->next();
479  if (precedes(braces, expr) && precedes(expr, braces->link()))
480  return false;
481  }
482  }
483  return true;
484 }
485 
486 static bool isEscapedReference(const Variable* var)
487 {
488  if (!var)
489  return false;
490  if (!var->isReference())
491  return false;
492  const Token * const varDeclEndToken = var->declEndToken();
493  if (!varDeclEndToken)
494  return false;
495  if (!Token::simpleMatch(varDeclEndToken, "="))
496  return false;
497  const Token* vartok = varDeclEndToken->astOperand2();
498  return !isTemporary(vartok, nullptr, false);
499 }
500 
501 static bool isDanglingSubFunction(const Token* tokvalue, const Token* tok)
502 {
503  if (!tokvalue)
504  return false;
505  const Variable* var = tokvalue->variable();
506  if (!var->isLocal())
507  return false;
508  const Function* f = Scope::nestedInFunction(tok->scope());
509  if (!f)
510  return false;
511  const Token* parent = tokvalue->astParent();
512  while (parent && !Token::Match(parent->previous(), "%name% (")) {
513  parent = parent->astParent();
514  }
515  if (!Token::simpleMatch(parent, "("))
516  return false;
517  return exprDependsOnThis(parent);
518 }
519 
520 static const Variable* getParentVar(const Token* tok)
521 {
522  if (!tok)
523  return nullptr;
524  if (Token::simpleMatch(tok, "."))
525  return getParentVar(tok->astOperand1());
526  return tok->variable();
527 }
528 
529 static bool isAssignedToNonLocal(const Token* tok)
530 {
531  if (!Token::simpleMatch(tok->astParent(), "="))
532  return false;
533  if (!astIsRHS(tok))
534  return false;
535  const Variable* var = getParentVar(tok->astParent()->astOperand1());
536  if (!var)
537  return false;
538  return !var->isLocal() || var->isStatic();
539 }
540 
541 void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end)
542 {
543  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
544  if (!start)
545  return;
546  const Scope * scope = start->scope();
547  if (!scope)
548  return;
549  // If the scope is not set correctly then skip checking it
550  if (scope->bodyStart != start)
551  return;
552  const bool returnRef = Function::returnsReference(scope->function);
553  for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
554  // Return reference from function
555  if (returnRef && Token::simpleMatch(tok->astParent(), "return")) {
556  for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok, *mSettings, true)) {
557  if (!printInconclusive && lt.inconclusive)
558  continue;
559  const Variable* var = lt.token->variable();
560  if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() &&
561  isInScope(var->nameToken(), tok->scope())) {
562  errorReturnReference(tok, lt.errorPath, lt.inconclusive);
563  break;
564  }
565  if (isDeadTemporary(lt.token, nullptr, &mSettings->library)) {
566  errorReturnTempReference(tok, lt.errorPath, lt.inconclusive);
567  break;
568  }
569  }
570  // Assign reference to non-local variable
571  } else if (Token::Match(tok->previous(), "&|&& %var% =") && tok->astParent() == tok->next() &&
572  tok->variable() && tok->variable()->nameToken() == tok &&
573  tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() &&
574  !tok->variable()->isArgument()) {
575  ErrorPath errorPath;
576  const Variable *var = ValueFlow::getLifetimeVariable(tok, errorPath, *mSettings);
577  if (var && isInScope(var->nameToken(), tok->scope())) {
578  errorDanglingReference(tok, var, std::move(errorPath));
579  continue;
580  }
581  // Reference to temporary
582  } else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) {
584  if (!printInconclusive && lt.inconclusive)
585  continue;
586  const Token * tokvalue = lt.token;
587  if (isDeadTemporary(tokvalue, tok, &mSettings->library)) {
588  errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive);
589  break;
590  }
591  }
592  }
593  const bool escape = Token::simpleMatch(tok->astParent(), "throw") ||
594  (Token::simpleMatch(tok->astParent(), "return") && !Function::returnsStandardType(scope->function));
595  std::unordered_set<const Token*> exprs;
596  for (const ValueFlow::Value& val:tok->values()) {
597  if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
598  continue;
599  if (!printInconclusive && val.isInconclusive())
600  continue;
601  const Token* parent = getParentLifetime(val.tokvalue, mSettings->library);
602  if (!exprs.insert(parent).second)
603  continue;
604  for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(parent, *mSettings, escape || isAssignedToNonLocal(tok))) {
605  const Token * tokvalue = lt.token;
606  if (val.isLocalLifetimeValue()) {
607  if (escape) {
608  if (getPointerDepth(tok) < getPointerDepth(tokvalue))
609  continue;
611  continue;
612  if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) &&
613  !astIsContainerView(tok->astParent()))
614  continue;
615  if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) &&
616  isInScope(tokvalue->variable()->nameToken(), scope)) ||
617  isDeadTemporary(tokvalue, nullptr, &mSettings->library)) {
618  errorReturnDanglingLifetime(tok, &val);
619  break;
620  }
621  } else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) {
622  errorInvalidLifetime(tok, &val);
623  break;
624  } else if (!tokvalue->variable() &&
625  isDeadTemporary(tokvalue, tok, &mSettings->library)) {
626  if (!diag(tokvalue))
627  errorDanglingTemporaryLifetime(tok, &val, tokvalue);
628  break;
629  }
630  }
631  if (tokvalue->variable() && (isInScope(tokvalue->variable()->nameToken(), tok->scope()) ||
632  (val.isSubFunctionLifetimeValue() && isDanglingSubFunction(tokvalue, tok)))) {
633  const Variable * var = nullptr;
634  const Token * tok2 = tok;
635  if (Token::simpleMatch(tok->astParent(), "=")) {
636  if (astIsRHS(tok)) {
637  var = getParentVar(tok->astParent()->astOperand1());
638  tok2 = tok->astParent()->astOperand1();
639  }
640  } else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) {
641  var = tok->variable();
642  }
644  continue;
645  const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop());
646  if (!nextTok)
647  nextTok = tok->next();
648  if (var && !var->isLocal() && !var->isArgument() && !(val.tokvalue && val.tokvalue->variable() && val.tokvalue->variable()->isStatic()) &&
649  !isVariableChanged(nextTok,
650  tok->scope()->bodyEnd,
651  var->declarationId(),
652  var->isGlobal(),
653  *mSettings)) {
654  errorDanglngLifetime(tok2, &val);
655  break;
656  }
657  }
658  }
659  }
660  const Token *lambdaEndToken = findLambdaEndToken(tok);
661  if (lambdaEndToken) {
662  checkVarLifetimeScope(lambdaEndToken->link(), lambdaEndToken);
663  tok = lambdaEndToken;
664  }
665  if (tok->str() == "{" && tok->scope()) {
666  // Check functions in local classes
667  if (tok->scope()->type == Scope::eClass ||
668  tok->scope()->type == Scope::eStruct ||
669  tok->scope()->type == Scope::eUnion) {
670  for (const Function& f:tok->scope()->functionList) {
671  if (f.functionScope)
672  checkVarLifetimeScope(f.functionScope->bodyStart, f.functionScope->bodyEnd);
673  }
674  tok = tok->link();
675  }
676  }
677  }
678 }
679 
681 {
682  logChecker("CheckAutoVariables::checkVarLifetime");
683  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
684  for (const Scope * scope : symbolDatabase->functionScopes) {
685  if (!scope->function)
686  continue;
687  checkVarLifetimeScope(scope->bodyStart, scope->bodyEnd);
688  }
689 }
690 
692 {
693  const bool inconclusive = val ? val->isInconclusive() : false;
694  ErrorPath errorPath = val ? val->errorPath : ErrorPath();
695  std::string msg = "Returning " + lifetimeMessage(tok, val, errorPath);
696  errorPath.emplace_back(tok, "");
697  reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
698 }
699 
701 {
702  const bool inconclusive = val ? val->isInconclusive() : false;
703  ErrorPath errorPath = val ? val->errorPath : ErrorPath();
704  std::string msg = "Using " + lifetimeMessage(tok, val, errorPath);
705  errorPath.emplace_back(tok, "");
706  reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
707 }
708 
710 {
711  const bool inconclusive = val ? val->isInconclusive() : false;
712  ErrorPath errorPath = val ? val->errorPath : ErrorPath();
713  std::string msg = "Using " + lifetimeMessage(tok, val, errorPath);
714  errorPath.emplace_back(tempTok, "Temporary created here.");
715  errorPath.emplace_back(tok, "");
716  reportError(errorPath,
718  "danglingTemporaryLifetime",
719  msg + " that is a temporary.",
720  CWE562,
722 }
723 
725 {
726  const bool inconclusive = val ? val->isInconclusive() : false;
727  ErrorPath errorPath = val ? val->errorPath : ErrorPath();
728  std::string tokName = tok ? tok->expressionString() : "x";
729  std::string msg = "Non-local variable '" + tokName + "' will use " + lifetimeMessage(tok, val, errorPath);
730  errorPath.emplace_back(tok, "");
731  reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
732 }
733 
734 void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
735 {
736  errorPath.emplace_back(tok, "");
737  reportError(
738  errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
739 }
740 
741 void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
742 {
743  errorPath.emplace_back(tok, "");
744  reportError(
745  errorPath, Severity::error, "returnReference", "Reference to local variable returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
746 }
747 
749 {
750  std::string tokName = tok ? tok->str() : "x";
751  std::string varName = var ? var->name() : "y";
752  std::string msg = "Non-local reference variable '" + tokName + "' to local variable '" + varName + "'";
753  errorPath.emplace_back(tok, "");
754  reportError(errorPath, Severity::error, "danglingReference", msg, CWE562, Certainty::normal);
755 }
756 
757 void CheckAutoVariables::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
758 {
759  errorPath.emplace_back(tok, "");
760  reportError(
761  errorPath, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal);
762 }
763 
765 {
766  const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr);
767 
768  std::string type = "an auto-variable";
769  if (tok && tok->tokType() == Token::eString)
770  type = "a string literal";
771  else if (val && val->tokvalue->tokType() == Token::eString)
772  type = "a pointer pointing to a string literal";
773  else if (var) {
774  if (var->isGlobal())
775  type = "a global variable";
776  else if (var->isStatic())
777  type = "a static variable";
778  }
779 
780  if (val)
781  type += " (" + val->tokvalue->str() + ")";
782 
783  reportError(getErrorPath(tok, val, "Deallocating memory that was not dynamically allocated"),
785  "autovarInvalidDeallocation",
786  "Deallocation of " + type + " results in undefined behaviour.\n"
787  "The deallocation of " + type + " results in undefined behaviour. You should only free memory "
788  "that has been allocated dynamically.", CWE590, Certainty::normal);
789 }
bool astIsContainerView(const Token *tok)
Definition: astutils.cpp:254
bool exprDependsOnThis(const Token *expr, bool onVar, nonneg int depth)
Definition: astutils.cpp:1098
bool isTemporary(const Token *tok, const Library *library, bool unknown)
Definition: astutils.cpp:415
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:987
bool astIsRHS(const Token *tok)
Definition: astutils.cpp:797
const Token * findLambdaEndToken(const Token *first)
find lambda function end token
Definition: astutils.cpp:3190
const Token * nextAfterAstRightmostLeaf(const Token *tok)
Definition: astutils.cpp:548
const Token * getParentLifetime(const Token *tok)
Definition: astutils.cpp:596
Library::Container::Yield astContainerYield(const Token *tok, const Token **ftok)
Definition: astutils.cpp:297
bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth)
Definition: astutils.cpp:2541
static const CWE CWE398(398U)
static bool isArrayVar(const Token *tok)
static bool isDeadScope(const Token *tok, const Scope *scope)
static bool isDeadTemporary(const Token *tok, const Token *expr, const Library *library)
static const Variable * getParentVar(const Token *tok)
static bool isAutoVar(const Token *tok)
static const CWE CWE590(590U)
static int getPointerDepth(const Token *tok)
static bool isEscapedReference(const Variable *var)
static bool isDanglingSubFunction(const Token *tokvalue, const Token *tok)
static bool isAutoVarArray(const Token *tok)
static bool isAssignedToNonLocal(const Token *tok)
static bool isInScope(const Token *tok, const Scope *scope)
static bool isAddressOfLocalVariable(const Token *expr)
static bool isAutoVariableRHS(const Token *tok)
static bool isArrayArg(const Token *tok, const Settings &settings)
static bool hasOverloadedAssignment(const Token *tok, bool &inconclusive)
static const CWE CWE562(562U)
static bool isNonReferenceArg(const Token *tok)
static bool isPtrArg(const Token *tok)
static bool isRefPtrArg(const Token *tok)
static bool variableIsUsedInScope(const Token *start, nonneg int varId, const Scope *scope)
static bool isLocalContainerBuffer(const Token *tok)
Various small checks for automatic variables.
void errorUselessAssignmentPtrArg(const Token *tok)
void errorReturnReference(const Token *tok, ErrorPath errorPath, bool inconclusive)
void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val)
void errorInvalidLifetime(const Token *tok, const ValueFlow::Value *val)
void errorDanglingTemporaryLifetime(const Token *tok, const ValueFlow::Value *val, const Token *tempTok)
void checkVarLifetimeScope(const Token *start, const Token *end)
void errorDanglingTempReference(const Token *tok, ErrorPath errorPath, bool inconclusive)
void errorUselessAssignmentArg(const Token *tok)
void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath)
void errorAutoVariableAssignment(const Token *tok, bool inconclusive)
void assignFunctionArg()
assign function argument
void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val)
bool diag(const Token *tokvalue)
returns true if tokvalue has already been diagnosed
bool checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken=nullptr)
Check variable assignment.
void autoVariables()
Check auto variables.
void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val)
std::set< const Token * > mDiagDanglingTemp
void errorReturnTempReference(const Token *tok, ErrorPath errorPath, bool inconclusive)
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
ErrorPath getErrorPath(const Token *errtok, const ValueFlow::Value *value, std::string bug) const
Definition: check.cpp:111
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
static bool returnsReference(const Function *function, bool unknown=false, bool includeRValueRef=false)
static bool returnsStandardType(const Function *function, bool unknown=false)
Library definitions handling.
Definition: library.h:52
bool isentrypoint(const std::string &func) const
Definition: library.h:427
const AllocFunc * getDeallocFuncInfo(const Token *tok) const
get deallocation info for function
Definition: library.cpp:1086
std::vector< Scope * > nestedList
static Function * nestedInFunction(const Scope *scope)
Function * function
function info for this function
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
std::string className
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
Library library
Library.
Definition: settings.h:237
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:557
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
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:722
nonneg int exprId() const
Definition: token.h:883
bool isC() const
Definition: token.cpp:2757
const std::string & originalName() const
Definition: token.h:1193
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1099
static std::pair< const Token *, const Token * > typeDecl(const Token *tok, bool pointedToType=false)
Definition: token.cpp:2431
const ValueType * valueType() const
Definition: token.h:331
void astOperand1(Token *tok)
Definition: token.cpp:1490
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
bool isCast() const
Definition: token.h:458
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
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:427
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1502
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:447
@ eString
Definition: token.h:162
@ eIncDecOp
Definition: token.h:163
Token * previous()
Definition: token.h:862
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
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:1471
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool isTokValue() const
Definition: vfvalue.h:214
ErrorPath errorPath
Definition: vfvalue.h:282
const Token * tokvalue
token value - the token that has the value.
Definition: vfvalue.h:271
bool isInconclusive() const
Definition: vfvalue.h:378
Value type.
enum ValueType::Type type
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isExtern() const
Is variable extern.
bool isReference() const
Is reference variable.
bool isRValueReference() const
Is reference variable.
bool isLocal() const
Is variable local.
const Token * declEndToken() const
Get end token of variable declaration E.g.
const Type * type() const
Get Type pointer of known type.
const Scope * scope() const
Get Scope pointer of enclosing scope.
bool isGlobal() const
Is variable global.
const std::string & name() const
Get name string.
bool isArray() const
Is variable an array.
const Token * nameToken() const
Get name token.
nonneg int declarationId() const
Get declaration ID (varId used for variable in its declaration).
bool isPointer() const
Is pointer variable.
bool isStatic() const
Is variable static.
const ValueType * valueType() const
#define nonneg
Definition: config.h:138
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130
@ warning
Warning.
@ style
Style warning.
@ error
Programming error.
std::vector< LifetimeToken > getLifetimeTokens(const Token *tok, const Settings &settings, bool escape=false, Value::ErrorPath errorPath=Value::ErrorPath{})
Definition: valueflow.cpp:3735
std::string lifetimeMessage(const Token *tok, const Value *val, Value::ErrorPath &errorPath)
Definition: valueflow.cpp:3490
bool isLifetimeBorrowed(const Token *tok, const Settings &settings)
Definition: valueflow.cpp:3907
const Variable * getLifetimeVariable(const Token *tok, Value::ErrorPath &errorPath, const Settings &settings, bool *addressOf=nullptr)
Definition: valueflow.cpp:3765