1 /*
2 * Cppcheck - A tool for static C/C++ code analysis
3 * Copyright (C) 2007-2021 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 //---------------------------------------------------------------------------
21 #include "checkuninitvar.h"
22
23 #include "astutils.h"
24 #include "checknullpointer.h" // CheckNullPointer::isPointerDeref
25 #include "errorlogger.h"
26 #include "library.h"
27 #include "mathlib.h"
28 #include "settings.h"
29 #include "symboldatabase.h"
30 #include "token.h"
31 #include "tokenize.h"
32 #include "valueflow.h"
33
34 #include <cassert>
35 #include <list>
36 #include <map>
37 #include <utility>
38
39
40 namespace tinyxml2 {
41 class XMLElement;
42 }
43
44 //---------------------------------------------------------------------------
45
46 // Register this check class (by creating a static instance of it)
47 namespace {
48 CheckUninitVar instance;
49 }
50
51 //---------------------------------------------------------------------------
52
isSizeOfEtc(const Token * tok)53 static bool isSizeOfEtc(const Token *tok)
54 {
55 return Token::Match(tok, "sizeof|typeof|offsetof|decltype|__typeof__ (");
56 }
57
58 // get ast parent, skip possible address-of and casts
getAstParentSkipPossibleCastAndAddressOf(const Token * vartok,bool * unknown)59 static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown)
60 {
61 if (unknown)
62 *unknown = false;
63 if (!vartok)
64 return nullptr;
65 const Token *parent = vartok->astParent();
66 while (Token::Match(parent, ".|::"))
67 parent = parent->astParent();
68 if (!parent)
69 return nullptr;
70 if (parent->isUnaryOp("&"))
71 parent = parent->astParent();
72 else if (parent->str() == "&" && vartok == parent->astOperand2() && Token::Match(parent->astOperand1()->previous(), "( %type% )")) {
73 parent = parent->astParent();
74 if (unknown)
75 *unknown = true;
76 }
77 while (parent && parent->isCast())
78 parent = parent->astParent();
79 return parent;
80 }
81
check()82 void CheckUninitVar::check()
83 {
84 const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
85
86 std::set<std::string> arrayTypeDefs;
87 for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
88 if (Token::Match(tok, "%name% [") && tok->variable() && Token::Match(tok->variable()->typeStartToken(), "%type% %var% ;"))
89 arrayTypeDefs.insert(tok->variable()->typeStartToken()->str());
90 }
91
92 // check every executable scope
93 for (const Scope &scope : symbolDatabase->scopeList) {
94 if (scope.isExecutable()) {
95 checkScope(&scope, arrayTypeDefs);
96 }
97 }
98 }
99
checkScope(const Scope * scope,const std::set<std::string> & arrayTypeDefs)100 void CheckUninitVar::checkScope(const Scope* scope, const std::set<std::string> &arrayTypeDefs)
101 {
102 for (const Variable &var : scope->varlist) {
103 if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) ||
104 var.isStatic() || var.isExtern() || var.isReference())
105 continue;
106
107 // don't warn for try/catch exception variable
108 if (var.isThrow())
109 continue;
110
111 if (Token::Match(var.nameToken()->next(), "[({:]"))
112 continue;
113
114 if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not
115 checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString);
116 continue;
117 }
118 if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not
119 checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString);
120 continue;
121 }
122
123 if (var.isArray() || var.isPointerToArray()) {
124 const Token *tok = var.nameToken()->next();
125 if (var.isPointerToArray())
126 tok = tok->next();
127 while (Token::simpleMatch(tok->link(), "] ["))
128 tok = tok->link()->next();
129 if (Token::Match(tok->link(), "] =|{"))
130 continue;
131 }
132
133 bool stdtype = mTokenizer->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end();
134 const Token* tok = var.typeStartToken();
135 for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) {
136 if (tok->isStandardType() || tok->isEnumType())
137 stdtype = true;
138 }
139 if (var.isArray() && !stdtype)
140 continue;
141
142 while (tok && tok->str() != ";")
143 tok = tok->next();
144 if (!tok)
145 continue;
146
147 if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") &&
148 checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true))
149 continue;
150
151 if (var.isArray()) {
152 Alloc alloc = ARRAY;
153 const std::map<nonneg int, VariableValue> variableValue;
154 bool init = false;
155 for (const Token *parent = var.nameToken(); parent; parent = parent->astParent()) {
156 if (parent->str() == "=") {
157 init = true;
158 break;
159 }
160 }
161 if (!init)
162 checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue);
163 continue;
164 }
165 if (stdtype || var.isPointer()) {
166 Alloc alloc = NO_ALLOC;
167 const std::map<nonneg int, VariableValue> variableValue;
168 checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue);
169 }
170 if (var.type())
171 checkStruct(tok, var);
172 }
173
174 if (scope->function) {
175 for (const Variable &arg : scope->function->argumentList) {
176 if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) {
177 // Treat the pointer as initialized until it is assigned by malloc
178 for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
179 if (!Token::Match(tok, "[;{}] %varid% =", arg.declarationId()))
180 continue;
181 const Token *allocFuncCallToken = findAllocFuncCallToken(tok->tokAt(2)->astOperand2(), mSettings->library);
182 if (!allocFuncCallToken)
183 continue;
184 const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken);
185 if (!allocFunc || allocFunc->initData)
186 continue;
187
188 if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType()))
189 checkStruct(tok, arg);
190 else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) {
191 Alloc alloc = NO_ALLOC;
192 const std::map<nonneg int, VariableValue> variableValue;
193 checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue);
194 }
195 }
196 }
197 }
198 }
199 }
200
checkStruct(const Token * tok,const Variable & structvar)201 void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar)
202 {
203 const Token *typeToken = structvar.typeStartToken();
204 const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase();
205 for (const Scope *scope2 : symbolDatabase->classAndStructScopes) {
206 if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) {
207 for (const Variable &var : scope2->varlist) {
208 if (var.isStatic() || var.hasDefault() || var.isArray() ||
209 (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::NeedInitialization::True)))
210 continue;
211
212 // is the variable declared in a inner union?
213 bool innerunion = false;
214 for (const Scope *innerScope : scope2->nestedList) {
215 if (innerScope->type == Scope::eUnion) {
216 if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() &&
217 var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) {
218 innerunion = true;
219 break;
220 }
221 }
222 }
223
224 if (!innerunion) {
225 Alloc alloc = NO_ALLOC;
226 const Token *tok2 = tok;
227 if (tok->str() == "}")
228 tok2 = tok2->next();
229 const std::map<nonneg int, VariableValue> variableValue;
230 checkScopeForVariable(tok2, structvar, nullptr, nullptr, &alloc, var.name(), variableValue);
231 }
232 }
233 }
234 }
235 }
236
operator !(VariableValue v)237 static VariableValue operator!(VariableValue v)
238 {
239 v.notEqual = !v.notEqual;
240 return v;
241 }
operator ==(const VariableValue & v,MathLib::bigint i)242 static bool operator==(const VariableValue & v, MathLib::bigint i)
243 {
244 return v.notEqual ? (i != v.value) : (i == v.value);
245 }
operator !=(const VariableValue & v,MathLib::bigint i)246 static bool operator!=(const VariableValue & v, MathLib::bigint i)
247 {
248 return v.notEqual ? (i == v.value) : (i != v.value);
249 }
250
conditionAlwaysTrueOrFalse(const Token * tok,const std::map<nonneg int,VariableValue> & variableValue,bool * alwaysTrue,bool * alwaysFalse)251 static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map<nonneg int, VariableValue> &variableValue, bool *alwaysTrue, bool *alwaysFalse)
252 {
253 if (!tok)
254 return;
255
256 if (tok->hasKnownIntValue()) {
257 if (tok->getKnownIntValue() == 0)
258 *alwaysFalse = true;
259 else
260 *alwaysTrue = true;
261 return;
262 }
263
264 if (tok->isName() || tok->str() == ".") {
265 while (tok && tok->str() == ".")
266 tok = tok->astOperand2();
267 const std::map<nonneg int, VariableValue>::const_iterator it = variableValue.find(tok ? tok->varId() : ~0U);
268 if (it != variableValue.end()) {
269 *alwaysTrue = (it->second != 0LL);
270 *alwaysFalse = (it->second == 0LL);
271 }
272 }
273
274 else if (tok->isComparisonOp()) {
275 if (variableValue.empty()) {
276 return;
277 }
278
279 const Token *vartok, *numtok;
280 if (tok->astOperand2() && tok->astOperand2()->isNumber()) {
281 vartok = tok->astOperand1();
282 numtok = tok->astOperand2();
283 } else if (tok->astOperand1() && tok->astOperand1()->isNumber()) {
284 vartok = tok->astOperand2();
285 numtok = tok->astOperand1();
286 } else {
287 return;
288 }
289
290 while (vartok && vartok->str() == ".")
291 vartok = vartok->astOperand2();
292
293 const std::map<nonneg int, VariableValue>::const_iterator it = variableValue.find(vartok ? vartok->varId() : ~0U);
294 if (it == variableValue.end())
295 return;
296
297 if (tok->str() == "==")
298 *alwaysTrue = (it->second == MathLib::toLongNumber(numtok->str()));
299 else if (tok->str() == "!=")
300 *alwaysTrue = (it->second != MathLib::toLongNumber(numtok->str()));
301 else
302 return;
303 *alwaysFalse = !(*alwaysTrue);
304 }
305
306 else if (tok->str() == "!") {
307 bool t=false,f=false;
308 conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t, &f);
309 if (t || f) {
310 *alwaysTrue = !t;
311 *alwaysFalse = !f;
312 }
313 }
314
315 else if (tok->str() == "||") {
316 bool t1=false, f1=false;
317 conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1);
318 bool t2=false, f2=false;
319 if (!t1)
320 conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2);
321 *alwaysTrue = (t1 || t2);
322 *alwaysFalse = (f1 && f2);
323 }
324
325 else if (tok->str() == "&&") {
326 bool t1=false, f1=false;
327 conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1);
328 bool t2=false, f2=false;
329 if (!f1)
330 conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2);
331 *alwaysTrue = (t1 && t2);
332 *alwaysFalse = (f1 || f2);
333 }
334 }
335
isVariableUsed(const Token * tok,const Variable & var)336 static bool isVariableUsed(const Token *tok, const Variable& var)
337 {
338 if (!tok)
339 return false;
340 if (tok->str() == "&" && !tok->astOperand2())
341 return false;
342 if (tok->isConstOp())
343 return isVariableUsed(tok->astOperand1(),var) || isVariableUsed(tok->astOperand2(),var);
344 if (tok->varId() != var.declarationId())
345 return false;
346 if (!var.isArray())
347 return true;
348
349 const Token *parent = tok->astParent();
350 while (Token::Match(parent, "[?:]"))
351 parent = parent->astParent();
352 // no dereference, then array is not "used"
353 if (!Token::Match(parent, "*|["))
354 return false;
355 const Token *parent2 = parent->astParent();
356 // TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays
357 return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent);
358 }
359
checkScopeForVariable(const Token * tok,const Variable & var,bool * const possibleInit,bool * const noreturn,Alloc * const alloc,const std::string & membervar,std::map<nonneg int,VariableValue> variableValue)360 bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map<nonneg int, VariableValue> variableValue)
361 {
362 const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable delaratkon, rather than a fundef
363 const bool printDebug = mSettings->debugwarnings;
364
365 if (possibleInit)
366 *possibleInit = false;
367
368 int number_of_if = 0;
369
370 if (var.declarationId() == 0U)
371 return true;
372
373 for (; tok; tok = tok->next()) {
374 // End of scope..
375 if (tok->str() == "}") {
376 if (number_of_if && possibleInit)
377 *possibleInit = true;
378
379 // might be a noreturn function..
380 if (mTokenizer->isScopeNoReturn(tok)) {
381 if (noreturn)
382 *noreturn = true;
383 return false;
384 }
385
386 break;
387 }
388
389 // Unconditional inner scope or try..
390 if (tok->str() == "{" && Token::Match(tok->previous(), ",|;|{|}|try")) {
391 bool possibleInitInner = false;
392 if (checkScopeForVariable(tok->next(), var, &possibleInitInner, noreturn, alloc, membervar, variableValue))
393 return true;
394 tok = tok->link();
395 if (possibleInitInner) {
396 number_of_if = 1;
397 if (possibleInit)
398 *possibleInit = true;
399 }
400 continue;
401 }
402
403 // assignment with nonzero constant..
404 if (Token::Match(tok->previous(), "[;{}] %var% = - %name% ;"))
405 variableValue[tok->varId()] = !VariableValue(0);
406
407 // Inner scope..
408 else if (Token::simpleMatch(tok, "if (")) {
409 bool alwaysTrue = false;
410 bool alwaysFalse = false;
411
412 // Is variable assigned in condition?
413 if (!membervar.empty()) {
414 for (const Token *cond = tok->linkAt(1); cond != tok; cond = cond->previous()) {
415 if (cond->varId() == var.declarationId() && isMemberVariableAssignment(cond, membervar))
416 return true;
417 }
418 }
419
420 conditionAlwaysTrueOrFalse(tok->next()->astOperand2(), variableValue, &alwaysTrue, &alwaysFalse);
421
422 // initialization / usage in condition..
423 if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), *alloc, membervar))
424 return true;
425
426 // checking if a not-zero variable is zero => bail out
427 nonneg int condVarId = 0;
428 VariableValue condVarValue(0);
429 const Token *condVarTok = nullptr;
430 if (alwaysFalse)
431 ;
432 else if (Token::simpleMatch(tok, "if (") &&
433 astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) {
434 const std::map<nonneg int,VariableValue>::const_iterator it = variableValue.find(condVarTok->varId());
435 if (it != variableValue.end() && it->second != 0)
436 return true; // this scope is not fully analysed => return true
437 else {
438 condVarId = condVarTok->varId();
439 condVarValue = !VariableValue(0);
440 }
441 } else if (Token::simpleMatch(tok, "if (") && Token::Match(tok->next()->astOperand2(), "==|!=")) {
442 const Token *condition = tok->next()->astOperand2();
443 const Token *lhs = condition->astOperand1();
444 const Token *rhs = condition->astOperand2();
445 const Token *vartok = (lhs && lhs->hasKnownIntValue()) ? rhs : lhs;
446 const Token *numtok = (lhs == vartok) ? rhs : lhs;
447 while (Token::simpleMatch(vartok, "."))
448 vartok = vartok->astOperand2();
449 if (vartok && vartok->varId() && numtok && numtok->hasKnownIntValue()) {
450 const std::map<nonneg int,VariableValue>::const_iterator it = variableValue.find(vartok->varId());
451 if (it != variableValue.end() && it->second != numtok->getKnownIntValue())
452 return true; // this scope is not fully analysed => return true
453 condVarId = vartok->varId();
454 condVarValue = VariableValue(numtok->getKnownIntValue());
455 if (condition->str() == "!=")
456 condVarValue = !condVarValue;
457 }
458 }
459
460 // goto the {
461 tok = tok->next()->link()->next();
462
463 if (!tok)
464 break;
465 if (tok->str() == "{") {
466 bool possibleInitIf((!alwaysTrue && number_of_if > 0) || suppressErrors);
467 bool noreturnIf = false;
468 const bool initif = !alwaysFalse && checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar, variableValue);
469
470 // bail out for such code:
471 // if (a) x=0; // conditional initialization
472 // if (b) return; // cppcheck doesn't know if b can be false when a is false.
473 // x++; // it's possible x is always initialized
474 if (!alwaysTrue && noreturnIf && number_of_if > 0) {
475 if (printDebug) {
476 std::string condition;
477 for (const Token *tok2 = tok->linkAt(-1); tok2 != tok; tok2 = tok2->next()) {
478 condition += tok2->str();
479 if (tok2->isName() && tok2->next()->isName())
480 condition += ' ';
481 }
482 reportError(tok, Severity::debug, "bailoutUninitVar", "bailout uninitialized variable checking for '" + var.name() + "'. can't determine if this condition can be false when previous condition is false: " + condition);
483 }
484 return true;
485 }
486
487 if (alwaysTrue && (initif || noreturnIf))
488 return true;
489
490 std::map<nonneg int, VariableValue> varValueIf;
491 if (!alwaysFalse && !initif && !noreturnIf) {
492 for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) {
493 if (Token::Match(tok2, "[;{}.] %name% = - %name% ;"))
494 varValueIf[tok2->next()->varId()] = !VariableValue(0);
495 else if (Token::Match(tok2, "[;{}.] %name% = %num% ;"))
496 varValueIf[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3)));
497 }
498 }
499
500 if (initif && condVarId > 0)
501 variableValue[condVarId] = !condVarValue;
502
503 // goto the }
504 tok = tok->link();
505
506 if (!Token::simpleMatch(tok, "} else {")) {
507 if (initif || possibleInitIf) {
508 ++number_of_if;
509 if (number_of_if >= 2)
510 return true;
511 }
512 } else {
513 // goto the {
514 tok = tok->tokAt(2);
515
516 bool possibleInitElse((!alwaysFalse && number_of_if > 0) || suppressErrors);
517 bool noreturnElse = false;
518 const bool initelse = !alwaysTrue && checkScopeForVariable(tok->next(), var, &possibleInitElse, &noreturnElse, alloc, membervar, variableValue);
519
520 std::map<nonneg int, VariableValue> varValueElse;
521 if (!alwaysTrue && !initelse && !noreturnElse) {
522 for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) {
523 if (Token::Match(tok2, "[;{}.] %var% = - %name% ;"))
524 varValueElse[tok2->next()->varId()] = !VariableValue(0);
525 else if (Token::Match(tok2, "[;{}.] %var% = %num% ;"))
526 varValueElse[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3)));
527 }
528 }
529
530 if (initelse && condVarId > 0 && !noreturnIf && !noreturnElse)
531 variableValue[condVarId] = condVarValue;
532
533 // goto the }
534 tok = tok->link();
535
536 if ((alwaysFalse || initif || noreturnIf) &&
537 (alwaysTrue || initelse || noreturnElse))
538 return true;
539
540 if (initif || initelse || possibleInitElse)
541 ++number_of_if;
542 if (!initif && !noreturnIf)
543 variableValue.insert(varValueIf.begin(), varValueIf.end());
544 if (!initelse && !noreturnElse)
545 variableValue.insert(varValueElse.begin(), varValueElse.end());
546 }
547 }
548 }
549
550 // = { .. }
551 else if (Token::simpleMatch(tok, "= {")) {
552 // end token
553 const Token *end = tok->next()->link();
554
555 // If address of variable is taken in the block then bail out
556 if (var.isPointer() || var.isArray()) {
557 if (Token::findmatch(tok->tokAt(2), "%varid%", end, var.declarationId()))
558 return true;
559 } else if (Token::findmatch(tok->tokAt(2), "& %varid%", end, var.declarationId())) {
560 return true;
561 }
562
563 const Token *errorToken = nullptr;
564 visitAstNodes(tok->next(),
565 [&](const Token *child) {
566 if (child->isUnaryOp("&"))
567 return ChildrenToVisit::none;
568 if (child->str() == "," || child->str() == "{" || child->isConstOp())
569 return ChildrenToVisit::op1_and_op2;
570 if (child->str() == "." && Token::Match(child->astOperand1(), "%varid%", var.declarationId()) && child->astOperand2() && child->astOperand2()->str() == membervar) {
571 errorToken = child;
572 return ChildrenToVisit::done;
573 }
574 return ChildrenToVisit::none;
575 });
576
577 if (errorToken) {
578 uninitStructMemberError(errorToken->astOperand2(), errorToken->astOperand1()->str() + "." + membervar);
579 return true;
580 }
581
582 // Skip block
583 tok = end;
584 continue;
585 }
586
587 // skip sizeof / offsetof
588 if (isSizeOfEtc(tok))
589 tok = tok->linkAt(1);
590
591 // for/while..
592 else if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) {
593 const bool forwhile = Token::Match(tok, "for|while (");
594
595 // is variable initialized in for-head?
596 if (forwhile && checkIfForWhileHead(tok->next(), var, tok->str() == "for", false, *alloc, membervar))
597 return true;
598
599 // goto the {
600 const Token *tok2 = forwhile ? tok->next()->link()->next() : tok->next();
601
602 if (tok2 && tok2->str() == "{") {
603 const bool init = checkLoopBody(tok2, var, *alloc, membervar, (number_of_if > 0) || suppressErrors);
604
605 // variable is initialized in the loop..
606 if (init)
607 return true;
608
609 // is variable used in for-head?
610 bool initcond = false;
611 if (!suppressErrors) {
612 const Token *startCond = forwhile ? tok->next() : tok->next()->link()->tokAt(2);
613 initcond = checkIfForWhileHead(startCond, var, false, bool(number_of_if == 0), *alloc, membervar);
614 }
615
616 // goto "}"
617 tok = tok2->link();
618
619 // do-while => goto ")"
620 if (!forwhile) {
621 // Assert that the tokens are '} while ('
622 if (!Token::simpleMatch(tok, "} while (")) {
623 if (printDebug)
624 reportError(tok,Severity::debug,"","assertion failed '} while ('");
625 break;
626 }
627
628 // Goto ')'
629 tok = tok->linkAt(2);
630
631 if (!tok)
632 // bailout : invalid code / bad tokenizer
633 break;
634
635 if (initcond)
636 // variable is initialized in while-condition
637 return true;
638 }
639 }
640 }
641
642 // Unknown or unhandled inner scope
643 else if (Token::simpleMatch(tok, ") {") || (Token::Match(tok, "%name% {") && tok->str() != "try")) {
644 if (tok->str() == "struct" || tok->str() == "union") {
645 tok = tok->linkAt(1);
646 continue;
647 }
648 return true;
649 }
650
651 // bailout if there is ({
652 if (Token::simpleMatch(tok, "( {")) {
653 return true;
654 }
655
656 // bailout if there is assembler code or setjmp
657 if (Token::Match(tok, "asm|setjmp (")) {
658 return true;
659 }
660
661 // bailout if there is a goto label
662 if (Token::Match(tok, "[;{}] %name% :")) {
663 return true;
664 }
665
666 if (tok->str() == "?") {
667 if (!tok->astOperand2())
668 return true;
669 const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
670 const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
671 const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
672 if (err)
673 uninitvarError(tok, var.nameToken()->str(), *alloc);
674
675 // Todo: skip expression if there is no error
676 return true;
677 }
678
679 if (Token::Match(tok, "return|break|continue|throw|goto")) {
680 if (noreturn)
681 *noreturn = true;
682
683 tok = tok->next();
684 while (tok && tok->str() != ";") {
685 // variable is seen..
686 if (tok->varId() == var.declarationId()) {
687 if (!membervar.empty()) {
688 if (!suppressErrors && Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && Token::Match(tok->next()->astParent(), "%cop%|return|throw|?"))
689 uninitStructMemberError(tok, tok->str() + "." + membervar);
690 else if (mTokenizer->isCPP() && !suppressErrors && Token::Match(tok, "%name%") && Token::Match(tok->astParent(), "return|throw|?"))
691 uninitStructMemberError(tok, tok->str() + "." + membervar);
692 }
693
694 // Use variable
695 else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc))
696 uninitvarError(tok, tok->str(), *alloc);
697
698 return true;
699 }
700
701 else if (isSizeOfEtc(tok))
702 tok = tok->linkAt(1);
703
704 else if (tok->str() == "?") {
705 if (!tok->astOperand2())
706 return true;
707 const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
708 const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
709 const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
710 if (err)
711 uninitvarError(tok, var.nameToken()->str(), *alloc);
712 return true;
713 }
714
715 tok = tok->next();
716 }
717
718 return (noreturn == nullptr);
719 }
720
721 // variable is seen..
722 if (tok->varId() == var.declarationId()) {
723 // calling function that returns uninit data through pointer..
724 if (var.isPointer() && Token::simpleMatch(tok->next(), "=")) {
725 const Token *rhs = tok->next()->astOperand2();
726 while (rhs && rhs->isCast())
727 rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1();
728 if (rhs && Token::Match(rhs->previous(), "%name% (")) {
729 const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(rhs->astOperand1());
730 if (allocFunc && !allocFunc->initData) {
731 *alloc = NO_CTOR_CALL;
732 continue;
733 }
734 }
735 }
736 if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) {
737 *alloc = CTOR_CALL;
738
739 // type has constructor(s)
740 if (var.typeScope() && var.typeScope()->numConstructors > 0)
741 return true;
742
743 // standard or enum type: check if new initializes the allocated memory
744 if (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType()) {
745 // scalar new with initialization
746 if (Token::Match(tok->next(), "= new %type% ("))
747 return true;
748
749 // array new
750 if (Token::Match(tok->next(), "= new %type% [") && Token::simpleMatch(tok->linkAt(4), "] ("))
751 return true;
752 }
753
754 continue;
755 }
756
757
758 if (!membervar.empty()) {
759 if (isMemberVariableAssignment(tok, membervar)) {
760 checkRhs(tok, var, *alloc, number_of_if, membervar);
761 return true;
762 }
763
764 if (isMemberVariableUsage(tok, var.isPointer(), *alloc, membervar)) {
765 uninitStructMemberError(tok, tok->str() + "." + membervar);
766 return true;
767 }
768
769 if (Token::Match(tok->previous(), "[(,] %name% [,)]"))
770 return true;
771
772 if (Token::Match(tok->previous(), "= %var% . %var% ;") && membervar == tok->strAt(2))
773 return true;
774
775 } else {
776 // Use variable
777 if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) {
778 uninitvarError(tok, tok->str(), *alloc);
779 return true;
780 }
781
782 else {
783 const Token *parent = tok;
784 while (parent->astParent() && ((astIsLHS(parent) && parent->astParent()->str() == "[") || parent->astParent()->isUnaryOp("*"))) {
785 parent = parent->astParent();
786 if (parent->str() == "[") {
787 if (const Token *errorToken = checkExpr(parent->astOperand2(), var, *alloc, number_of_if==0)) {
788 if (!suppressErrors)
789 uninitvarError(errorToken, errorToken->expressionString(), *alloc);
790 return true;
791 }
792 }
793 }
794 if (Token::simpleMatch(parent->astParent(), "=") && astIsLHS(parent)) {
795 const Token *eq = parent->astParent();
796 if (const Token *errorToken = checkExpr(eq->astOperand2(), var, *alloc, number_of_if==0)) {
797 if (!suppressErrors)
798 uninitvarError(errorToken, errorToken->expressionString(), *alloc);
799 return true;
800 }
801 }
802
803 // assume that variable is assigned
804 return true;
805 }
806 }
807 }
808 }
809
810 return false;
811 }
812
checkExpr(const Token * tok,const Variable & var,const Alloc alloc,bool known,bool * bailout)813 const Token *CheckUninitVar::checkExpr(const Token *tok, const Variable& var, const Alloc alloc, bool known, bool *bailout)
814 {
815 if (!tok)
816 return nullptr;
817 if (isSizeOfEtc(tok->previous()))
818 return nullptr;
819
820 if (tok->astOperand1()) {
821 bool bailout1 = false;
822 const Token *errorToken = checkExpr(tok->astOperand1(), var, alloc, known, &bailout1);
823 if (bailout && bailout1)
824 *bailout = true;
825 if (errorToken)
826 return errorToken;
827 if ((bailout1 || !known) && Token::Match(tok, "%oror%|&&|?"))
828 return nullptr;
829 }
830 if (tok->astOperand2())
831 return checkExpr(tok->astOperand2(), var, alloc, known, bailout);
832 if (tok->varId() == var.declarationId()) {
833 const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc);
834 if (errorToken)
835 return errorToken;
836 else if (bailout)
837 *bailout = true;
838 }
839 return nullptr;
840 }
841
checkIfForWhileHead(const Token * startparentheses,const Variable & var,bool suppressErrors,bool isuninit,Alloc alloc,const std::string & membervar)842 bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar)
843 {
844 const Token * const endpar = startparentheses->link();
845 if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0))
846 suppressErrors = true;
847 for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) {
848 if (tok->varId() == var.declarationId()) {
849 if (Token::Match(tok, "%name% . %name%")) {
850 if (membervar.empty())
851 return true;
852 if (tok->strAt(2) == membervar) {
853 if (isMemberVariableAssignment(tok, membervar))
854 return true;
855
856 if (!suppressErrors && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar))
857 uninitStructMemberError(tok, tok->str() + "." + membervar);
858 }
859 continue;
860 }
861
862 if (const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc)) {
863 if (suppressErrors)
864 continue;
865 uninitvarError(errorToken, errorToken->expressionString(), alloc);
866 }
867 return true;
868 }
869 // skip sizeof / offsetof
870 if (isSizeOfEtc(tok))
871 tok = tok->linkAt(1);
872 if ((!isuninit || !membervar.empty()) && tok->str() == "&&")
873 suppressErrors = true;
874 }
875 return false;
876 }
877
878 /** recursively check loop, return error token */
checkLoopBodyRecursive(const Token * start,const Variable & var,const Alloc alloc,const std::string & membervar,bool & bailout) const879 const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const
880 {
881 assert(start->str() == "{");
882
883 const Token *errorToken = nullptr;
884
885 const Token *const end = start->link();
886 for (const Token *tok = start->next(); tok != end; tok = tok->next()) {
887 // skip sizeof / offsetof
888 if (isSizeOfEtc(tok)) {
889 tok = tok->linkAt(1);
890 continue;
891 }
892
893 if (Token::Match(tok, "asm ( %str% ) ;")) {
894 bailout = true;
895 return nullptr;
896 }
897
898 // for loop; skip third expression until loop body has been analyzed..
899 if (tok->str() == ";" && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(")) {
900 const Token *top = tok->astParent()->astParent();
901 if (!Token::simpleMatch(top->previous(), "for (") || !Token::simpleMatch(top->link(), ") {"))
902 continue;
903 const Token *bodyStart = top->link()->next();
904 const Token *errorToken1 = checkLoopBodyRecursive(bodyStart, var, alloc, membervar, bailout);
905 if (!errorToken)
906 errorToken = errorToken1;
907 if (bailout)
908 return nullptr;
909 }
910 // for loop; skip loop body if there is third expression
911 if (Token::simpleMatch(tok, ") {") &&
912 Token::simpleMatch(tok->link()->previous(), "for (") &&
913 Token::simpleMatch(tok->link()->astOperand2(), ";") &&
914 Token::simpleMatch(tok->link()->astOperand2()->astOperand2(), ";")) {
915 tok = tok->linkAt(1);
916 }
917
918 if (tok->str() == "{") {
919 // switch => bailout
920 if (tok->scope() && tok->scope()->type == Scope::ScopeType::eSwitch) {
921 bailout = true;
922 return nullptr;
923 }
924
925 const Token *errorToken1 = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout);
926 tok = tok->link();
927 if (Token::simpleMatch(tok, "} else {")) {
928 const Token *elseBody = tok->tokAt(2);
929 const Token *errorToken2 = checkLoopBodyRecursive(elseBody, var, alloc, membervar, bailout);
930 tok = elseBody->link();
931 if (errorToken1 && errorToken2)
932 return errorToken1;
933 if (errorToken2)
934 errorToken = errorToken2;
935 }
936 if (bailout)
937 return nullptr;
938 if (!errorToken)
939 errorToken = errorToken1;
940 }
941
942 if (tok->varId() != var.declarationId())
943 continue;
944
945 bool conditionalUsage = false;
946 for (const Token* parent = tok; parent; parent = parent->astParent()) {
947 if (Token::Match(parent->astParent(), "%oror%|&&|?") && astIsRHS(parent)) {
948 conditionalUsage = true;
949 break;
950 }
951 }
952
953 if (!membervar.empty()) {
954 if (isMemberVariableAssignment(tok, membervar)) {
955 bool assign = true;
956 bool rhs = false;
957 // Used for tracking if an ")" is inner or outer
958 const Token *rpar = nullptr;
959 for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
960 if (tok2->str() == "=")
961 rhs = true;
962
963 // Look at inner expressions but not outer expressions
964 if (!rpar && tok2->str() == "(")
965 rpar = tok2->link();
966 else if (tok2->str() == ")") {
967 // No rpar => this is an outer right parenthesis
968 if (!rpar)
969 break;
970 if (rpar == tok2)
971 rpar = nullptr;
972 }
973
974 if (tok2->str() == ";" || (!rpar && tok2->str() == ","))
975 break;
976 if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) {
977 assign = false;
978 break;
979 }
980 }
981 if (assign) {
982 bailout = true;
983 return nullptr;
984 }
985 }
986 if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) {
987 if (!conditionalUsage)
988 return tok;
989 if (!errorToken)
990 errorToken = tok;
991 } else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) {
992 bailout = true;
993 return nullptr;
994 }
995 } else {
996 if (const Token *errtok = isVariableUsage(tok, var.isPointer(), alloc)) {
997 if (!conditionalUsage)
998 return errtok;
999 if (!errorToken)
1000 errorToken = errtok;
1001 } else if (tok->strAt(1) == "=") {
1002 bool varIsUsedInRhs = false;
1003 visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) {
1004 if (!t)
1005 return ChildrenToVisit::none;
1006 if (t->varId() == var.declarationId()) {
1007 varIsUsedInRhs = true;
1008 return ChildrenToVisit::done;
1009 }
1010 if (isSizeOfEtc(t->previous()))
1011 return ChildrenToVisit::none;
1012 return ChildrenToVisit::op1_and_op2;
1013 });
1014 if (!varIsUsedInRhs) {
1015 bailout = true;
1016 return nullptr;
1017 }
1018 } else {
1019 bailout = true;
1020 return nullptr;
1021 }
1022 }
1023 }
1024
1025 return errorToken;
1026 }
1027
checkLoopBody(const Token * tok,const Variable & var,const Alloc alloc,const std::string & membervar,const bool suppressErrors)1028 bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors)
1029 {
1030 bool bailout = false;
1031 const Token *errorToken = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout);
1032
1033 if (!suppressErrors && !bailout && errorToken) {
1034 if (membervar.empty())
1035 uninitvarError(errorToken, errorToken->expressionString(), alloc);
1036 else
1037 uninitStructMemberError(errorToken, errorToken->expressionString() + "." + membervar);
1038 return true;
1039 }
1040
1041 return bailout;
1042 }
1043
checkRhs(const Token * tok,const Variable & var,Alloc alloc,nonneg int number_of_if,const std::string & membervar)1044 void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar)
1045 {
1046 bool rhs = false;
1047 int indent = 0;
1048 while (nullptr != (tok = tok->next())) {
1049 if (tok->str() == "=")
1050 rhs = true;
1051 else if (rhs && tok->varId() == var.declarationId()) {
1052 if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc))
1053 uninitvarError(tok, tok->str(), alloc);
1054 else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar))
1055 uninitStructMemberError(tok, tok->str() + "." + membervar);
1056 else if (Token::Match(tok, "%var% ="))
1057 break;
1058 else if (Token::Match(tok->previous(), "[(,&]"))
1059 break;
1060 } else if (tok->str() == ";" || (indent==0 && tok->str() == ","))
1061 break;
1062 else if (tok->str() == "(")
1063 ++indent;
1064 else if (tok->str() == ")") {
1065 if (indent == 0)
1066 break;
1067 --indent;
1068 } else if (tok->str() == "?" && tok->astOperand2()) {
1069 const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
1070 const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
1071 const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
1072 if (err)
1073 uninitvarError(tok, var.nameToken()->str(), alloc);
1074 break;
1075 } else if (isSizeOfEtc(tok))
1076 tok = tok->linkAt(1);
1077
1078 }
1079 }
1080
astIsLhs(const Token * tok)1081 static bool astIsLhs(const Token *tok)
1082 {
1083 return tok && tok->astParent() && tok == tok->astParent()->astOperand1();
1084 }
1085
astIsRhs(const Token * tok)1086 static bool astIsRhs(const Token *tok)
1087 {
1088 return tok && tok->astParent() && tok == tok->astParent()->astOperand2();
1089 }
1090
isVoidCast(const Token * tok)1091 static bool isVoidCast(const Token *tok)
1092 {
1093 return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0;
1094 }
1095
isVariableUsage(bool cpp,const Token * vartok,const Library & library,bool pointer,Alloc alloc,int indirect)1096 const Token* CheckUninitVar::isVariableUsage(bool cpp, const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
1097 {
1098 const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable
1099 while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr))
1100 valueExpr = valueExpr->astParent();
1101 // stuff we ignore..
1102 while (valueExpr->astParent()) {
1103 // *&x
1104 if (valueExpr->astParent()->isUnaryOp("&") && valueExpr->astParent()->astParent() && valueExpr->astParent()->astParent()->isUnaryOp("*"))
1105 valueExpr = valueExpr->astParent()->astParent();
1106 // (type &)x
1107 else if (valueExpr->astParent()->isCast() && valueExpr->astParent()->isUnaryOp("(") && Token::simpleMatch(valueExpr->astParent()->link()->previous(), "& )"))
1108 valueExpr = valueExpr->astParent();
1109 else
1110 break;
1111 }
1112 if (!pointer) {
1113 if (Token::Match(vartok, "%name% [.(]") && vartok->variable() && !vartok->variable()->isPointer())
1114 return nullptr;
1115 while (Token::simpleMatch(valueExpr->astParent(), ".") && astIsLhs(valueExpr) && valueExpr->astParent()->valueType() && valueExpr->astParent()->valueType()->pointer == 0)
1116 valueExpr = valueExpr->astParent();
1117 }
1118 const Token *derefValue = nullptr; // dereferenced value expression
1119 if (alloc != NO_ALLOC) {
1120 const int arrayDim = (vartok->variable() && vartok->variable()->isArray()) ? vartok->variable()->dimensions().size() : 1;
1121 int deref = 0;
1122 derefValue = valueExpr;
1123 while (Token::Match(derefValue->astParent(), "+|-|*|[|.") ||
1124 (derefValue->astParent() && derefValue->astParent()->isCast()) ||
1125 (deref < arrayDim && Token::simpleMatch(derefValue->astParent(), "&") && derefValue->astParent()->isBinaryOp())) {
1126 const Token * const derefValueParent = derefValue->astParent();
1127 if (derefValueParent->str() == "*") {
1128 if (derefValueParent->isUnaryOp("*"))
1129 ++deref;
1130 else
1131 break;
1132 } else if (derefValueParent->str() == "[") {
1133 if (astIsLhs(derefValue))
1134 ++deref;
1135 else
1136 break;
1137 } else if (Token::Match(derefValueParent, "[+-]")) {
1138 if (deref >= arrayDim)
1139 break;
1140 } else if (derefValueParent->str() == ".")
1141 ++deref;
1142 derefValue = derefValueParent;
1143 if (deref < arrayDim)
1144 valueExpr = derefValue;
1145 }
1146 if (deref < arrayDim) {
1147 // todo compare deref with array dimensions
1148 derefValue = nullptr;
1149 }
1150 } else if (vartok->astParent() && vartok->astParent()->isUnaryOp("&")) {
1151 const Token *child = vartok->astParent();
1152 const Token *parent = child->astParent();
1153 while (parent && (parent->isCast() || parent->str() == "+")) {
1154 child = parent;
1155 parent = child->astParent();
1156 }
1157 if (parent && (parent->isUnaryOp("*") || (parent->str() == "[" && astIsLhs(child))))
1158 derefValue = parent;
1159 }
1160
1161 if (!valueExpr->astParent())
1162 return nullptr;
1163
1164 // FIXME handle address of!!
1165 if (derefValue && derefValue->astParent() && derefValue->astParent()->isUnaryOp("&"))
1166 return nullptr;
1167
1168 // BAILOUT for binary & without parent
1169 if (Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr) && Token::Match(valueExpr->astParent()->tokAt(-3), "( %name% ) &"))
1170 return nullptr;
1171
1172 // safe operations
1173 if (isVoidCast(valueExpr->astParent()))
1174 return nullptr;
1175 if (Token::simpleMatch(valueExpr->astParent(), ".")) {
1176 const Token *parent = valueExpr->astParent();
1177 while (Token::simpleMatch(parent, "."))
1178 parent = parent->astParent();
1179 if (isVoidCast(parent))
1180 return nullptr;
1181 }
1182 if (alloc != NO_ALLOC) {
1183 if (Token::Match(valueExpr->astParent(), "%comp%|%oror%|&&|?|!"))
1184 return nullptr;
1185 if (Token::Match(valueExpr->astParent(), "%or%|&") && valueExpr->astParent()->isBinaryOp())
1186 return nullptr;
1187 if (alloc == CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue))
1188 return nullptr;
1189 if (Token::simpleMatch(valueExpr->astParent(), "return"))
1190 return nullptr;
1191 }
1192
1193 // Passing variable to function..
1194 if (Token::Match(valueExpr->astParent(), "[(,]") && (valueExpr->astParent()->str() == "," || astIsRhs(valueExpr))) {
1195 const Token *parent = valueExpr->astParent();
1196 while (Token::simpleMatch(parent, ","))
1197 parent = parent->astParent();
1198 if (Token::simpleMatch(parent, "{"))
1199 return valueExpr;
1200 const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
1201 return (use>0) ? valueExpr : nullptr;
1202 }
1203 if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) {
1204 const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect);
1205 return (use>0) ? derefValue : nullptr;
1206 }
1207 if (valueExpr->astParent()->isUnaryOp("&")) {
1208 const Token *parent = valueExpr->astParent();
1209 if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) {
1210 const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
1211 return (use>0) ? valueExpr : nullptr;
1212 }
1213 return nullptr;
1214 }
1215
1216 // Assignments;
1217 // * Is this LHS in assignment
1218 // * Passing address in RHS to pointer variable
1219 {
1220 const Token *tok = derefValue ? derefValue : valueExpr;
1221 if (alloc == NO_ALLOC) {
1222 while (tok->valueType() && tok->valueType()->pointer == 0 && Token::simpleMatch(tok->astParent(), "."))
1223 tok = tok->astParent();
1224 }
1225 if (Token::simpleMatch(tok->astParent(), "=")) {
1226 if (astIsLhs(tok))
1227 return nullptr;
1228 if (alloc != NO_ALLOC && astIsRhs(valueExpr))
1229 return nullptr;
1230 }
1231 }
1232
1233 // Initialize reference variable
1234 if (Token::Match((derefValue ? derefValue : vartok)->astParent(), "(|=") && astIsRhs(derefValue ? derefValue : vartok)) {
1235 const Token *rhstok = derefValue ? derefValue : vartok;
1236 const Token *lhstok = rhstok->astParent()->astOperand1();
1237 const Variable *lhsvar = lhstok->variable();
1238 if (lhsvar && lhsvar->isReference() && lhsvar->nameToken() == lhstok)
1239 return nullptr;
1240 }
1241
1242 // range for loop
1243 if (Token::simpleMatch(valueExpr->astParent(), ":") &&
1244 valueExpr->astParent()->astParent() &&
1245 Token::simpleMatch(valueExpr->astParent()->astParent()->previous(), "for (")) {
1246 if (astIsLhs(valueExpr))
1247 return nullptr;
1248 // Taking value by reference?
1249 const Token *lhs = valueExpr->astParent()->astOperand1();
1250 if (lhs && lhs->variable() && lhs->variable()->nameToken() == lhs && lhs->variable()->isReference())
1251 return nullptr;
1252 }
1253
1254 // Stream read/write
1255 // FIXME this code is a hack!!
1256 if (cpp && Token::Match(valueExpr->astParent(), "<<|>>")) {
1257 if (isLikelyStreamRead(cpp, vartok->previous()))
1258 return nullptr;
1259
1260 if (valueExpr->valueType() && valueExpr->valueType()->type == ValueType::Type::VOID)
1261 return nullptr;
1262 }
1263 if (astIsRhs(derefValue) && isLikelyStreamRead(cpp, derefValue->astParent()))
1264 return nullptr;
1265
1266 // Assignment with overloaded &
1267 if (cpp && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) {
1268 const Token *parent = valueExpr->astParent();
1269 while (Token::simpleMatch(parent, "&") && parent->isBinaryOp())
1270 parent = parent->astParent();
1271 if (!parent) {
1272 const Token *lhs = valueExpr->astParent();
1273 while (Token::simpleMatch(lhs, "&") && lhs->isBinaryOp())
1274 lhs = lhs->astOperand1();
1275 if (lhs && lhs->isName() && (!lhs->valueType() || lhs->valueType()->type <= ValueType::Type::CONTAINER))
1276 return nullptr; // <- possible assignment
1277 }
1278 }
1279
1280 return derefValue ? derefValue : valueExpr;
1281 }
1282
isVariableUsage(const Token * vartok,bool pointer,Alloc alloc,int indirect) const1283 const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
1284 {
1285 return CheckUninitVar::isVariableUsage(mTokenizer->isCPP(), vartok, mSettings->library, pointer, alloc, indirect);
1286 }
1287
1288 /***
1289 * Is function parameter "used" so a "usage of uninitialized variable" can
1290 * be written? If parameter is passed "by value" then it is "used". If it
1291 * is passed "by reference" then it is not necessarily "used".
1292 * @return -1 => unknown 0 => not used 1 => used
1293 */
isFunctionParUsage(const Token * vartok,const Library & library,bool pointer,Alloc alloc,int indirect)1294 int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
1295 {
1296 bool unknown = false;
1297 const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown);
1298 if (unknown || !Token::Match(parent, "[(,]"))
1299 return -1;
1300
1301 // locate start parentheses in function call..
1302 int argumentNumber = 0;
1303 const Token *start = vartok;
1304 while (start && !Token::Match(start, "[;{}(]")) {
1305 if (start->str() == ")")
1306 start = start->link();
1307 else if (start->str() == ",")
1308 ++argumentNumber;
1309 start = start->previous();
1310 }
1311 if (!start)
1312 return -1;
1313
1314 if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch"))
1315 return (!pointer || alloc == NO_ALLOC);
1316
1317 // is this a function call?
1318 if (Token::Match(start->previous(), "%name% (")) {
1319 const bool address(vartok->previous()->str() == "&");
1320 const bool array(vartok->variable() && vartok->variable()->isArray());
1321 // check how function handle uninitialized data arguments..
1322 const Function *func = start->previous()->function();
1323 if (func) {
1324 const Variable *arg = func->getArgumentVar(argumentNumber);
1325 if (arg) {
1326 const Token *argStart = arg->typeStartToken();
1327 if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]"))
1328 return 1;
1329 if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]"))
1330 return 1;
1331 while (argStart->previous() && argStart->previous()->isName())
1332 argStart = argStart->previous();
1333 if (Token::Match(argStart, "const %type% & %name% [,)]")) {
1334 // If it's a record it's ok to pass a partially uninitialized struct.
1335 if (vartok->variable() && vartok->variable()->valueType() && vartok->variable()->valueType()->type == ValueType::Type::RECORD)
1336 return -1;
1337 return 1;
1338 }
1339 if ((pointer || address) && Token::Match(argStart, "const %type% %name% [") && Token::Match(argStart->linkAt(3), "] [,)]"))
1340 return 1;
1341 }
1342
1343 } else if (Token::Match(start->previous(), "if|while|for")) {
1344 // control-flow statement reading the variable "by value"
1345 return alloc == NO_ALLOC;
1346 } else {
1347 const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1);
1348 if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC)
1349 return 1;
1350 bool hasIndirect = false;
1351 const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect);
1352 if (alloc != NO_ALLOC)
1353 return (isnullbad || hasIndirect) && isuninitbad;
1354 return isuninitbad && (!address || isnullbad);
1355 }
1356 }
1357
1358 // unknown
1359 return -1;
1360 }
1361
isFunctionParUsage(const Token * vartok,bool pointer,Alloc alloc,int indirect) const1362 int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
1363 {
1364 return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect);
1365 }
1366
isMemberVariableAssignment(const Token * tok,const std::string & membervar) const1367 bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const
1368 {
1369 if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) {
1370 if (Token::Match(tok->tokAt(3), "[=.[]"))
1371 return true;
1372 else if (Token::Match(tok->tokAt(-2), "[(,=] &"))
1373 return true;
1374 else if (isLikelyStreamRead(mTokenizer->isCPP(), tok->previous()))
1375 return true;
1376 else if ((tok->previous() && tok->previous()->isConstOp()) || Token::Match(tok->previous(), "[|="))
1377 ; // member variable usage
1378 else if (tok->tokAt(3)->isConstOp())
1379 ; // member variable usage
1380 else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") &&
1381 1 == isFunctionParUsage(tok, false, NO_ALLOC)) {
1382 return false;
1383 } else
1384 return true;
1385 } else if (tok->strAt(1) == "=")
1386 return true;
1387 else if (Token::Match(tok, "%var% . %name% (")) {
1388 const Token *ftok = tok->tokAt(2);
1389 if (!ftok->function() || !ftok->function()->isConst())
1390 // TODO: Try to determine if membervar is assigned in method
1391 return true;
1392 } else if (tok->strAt(-1) == "&") {
1393 if (Token::Match(tok->tokAt(-2), "[(,] & %name%")) {
1394 // locate start parentheses in function call..
1395 int argumentNumber = 0;
1396 const Token *ftok = tok;
1397 while (ftok && !Token::Match(ftok, "[;{}(]")) {
1398 if (ftok->str() == ")")
1399 ftok = ftok->link();
1400 else if (ftok->str() == ",")
1401 ++argumentNumber;
1402 ftok = ftok->previous();
1403 }
1404
1405 // is this a function call?
1406 ftok = ftok ? ftok->previous() : nullptr;
1407 if (Token::Match(ftok, "%name% (")) {
1408 // check how function handle uninitialized data arguments..
1409 const Function *function = ftok->function();
1410
1411 if (!function && mSettings) {
1412 // Function definition not seen, check if direction is specified in the library configuration
1413 const Library::ArgumentChecks::Direction argDirection = mSettings->library.getArgDirection(ftok, 1 + argumentNumber);
1414 if (argDirection == Library::ArgumentChecks::Direction::DIR_IN)
1415 return false;
1416 else if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT)
1417 return true;
1418 }
1419
1420 const Variable *arg = function ? function->getArgumentVar(argumentNumber) : nullptr;
1421 const Token *argStart = arg ? arg->typeStartToken() : nullptr;
1422 while (argStart && argStart->previous() && argStart->previous()->isName())
1423 argStart = argStart->previous();
1424 if (Token::Match(argStart, "const struct| %type% * const| %name% [,)]"))
1425 return false;
1426 }
1427
1428 else if (ftok && Token::simpleMatch(ftok->previous(), "= * ("))
1429 return false;
1430 }
1431 return true;
1432 }
1433 return false;
1434 }
1435
isMemberVariableUsage(const Token * tok,bool isPointer,Alloc alloc,const std::string & membervar) const1436 bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const
1437 {
1438 if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") &&
1439 tok->strAt(2) == membervar) {
1440 const int use = isFunctionParUsage(tok, isPointer, alloc);
1441 if (use == 1)
1442 return true;
1443 }
1444
1445 if (isMemberVariableAssignment(tok, membervar))
1446 return false;
1447
1448 if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) {
1449 const Token *parent = tok->next()->astParent();
1450 if (parent && parent->isUnaryOp("&"))
1451 return false;
1452 return true;
1453 } else if (!isPointer && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc))
1454 return true;
1455
1456 else if (!isPointer && Token::Match(tok->previous(), "= %name% ;"))
1457 return true;
1458
1459 // = *(&var);
1460 else if (!isPointer &&
1461 Token::simpleMatch(tok->astParent(),"&") &&
1462 Token::simpleMatch(tok->astParent()->astParent(),"*") &&
1463 Token::Match(tok->astParent()->astParent()->astParent(), "= * (| &") &&
1464 tok->astParent()->astParent()->astParent()->astOperand2() == tok->astParent()->astParent())
1465 return true;
1466
1467 else if (mSettings->certainty.isEnabled(Certainty::experimental) &&
1468 !isPointer &&
1469 Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") &&
1470 isVariableUsage(tok, isPointer, alloc))
1471 return true;
1472
1473 return false;
1474 }
1475
uninitstringError(const Token * tok,const std::string & varname,bool strncpy_)1476 void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_)
1477 {
1478 reportError(tok, Severity::error, "uninitstring", "$symbol:" + varname + "\nDangerous usage of '$symbol'" + (strncpy_ ? " (strncpy doesn't always null-terminate it)." : " (not null-terminated)."), CWE_USE_OF_POTENTIALLY_DANGEROUS_FUNCTION, Certainty::normal);
1479 }
1480
uninitdataError(const Token * tok,const std::string & varname)1481 void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname)
1482 {
1483 reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
1484 }
1485
uninitvarError(const Token * tok,const std::string & varname,ErrorPath errorPath)1486 void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath)
1487 {
1488 errorPath.emplace_back(tok, "");
1489 reportError(errorPath, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
1490 }
1491
uninitStructMemberError(const Token * tok,const std::string & membername)1492 void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername)
1493 {
1494 reportError(tok,
1495 Severity::error,
1496 "uninitStructMember",
1497 "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
1498 }
1499
isLeafDot(const Token * tok)1500 static bool isLeafDot(const Token* tok)
1501 {
1502 if (!tok)
1503 return false;
1504 const Token * parent = tok->astParent();
1505 if (!Token::simpleMatch(parent, "."))
1506 return false;
1507 if (parent->astOperand2() == tok)
1508 return true;
1509 return isLeafDot(parent);
1510 }
1511
valueFlowUninit()1512 void CheckUninitVar::valueFlowUninit()
1513 {
1514 const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1515
1516 // check every executable scope
1517 for (const Scope *scope : symbolDatabase->functionScopes) {
1518 for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1519 if (isSizeOfEtc(tok)) {
1520 tok = tok->linkAt(1);
1521 continue;
1522 }
1523 if (!tok->variable() && !tok->isUnaryOp("*"))
1524 continue;
1525 if (Token::Match(tok, "%name% ("))
1526 continue;
1527 const Token* parent = tok->astParent();
1528 while (Token::simpleMatch(parent, "."))
1529 parent = parent->astParent();
1530 if (parent && parent->isUnaryOp("&"))
1531 continue;
1532 if (isVoidCast(parent))
1533 continue;
1534 auto v = std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isUninitValue));
1535 if (v == tok->values().end())
1536 continue;
1537 if (v->isInconclusive())
1538 continue;
1539 if (v->indirect > 1 || v->indirect < 0)
1540 continue;
1541 bool uninitderef = false;
1542 if (tok->variable()) {
1543 bool unknown;
1544 const bool isarray = !tok->variable() || tok->variable()->isArray();
1545 const bool ispointer = astIsPointer(tok) && !isarray;
1546 const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, mSettings);
1547 if (ispointer && v->indirect == 1 && !deref)
1548 continue;
1549 if (isarray && !deref)
1550 continue;
1551 uninitderef = deref && v->indirect == 0;
1552 const bool isleaf = isLeafDot(tok) || uninitderef;
1553 if (Token::Match(tok->astParent(), ". %var%") && !isleaf)
1554 continue;
1555 }
1556 if (!(Token::Match(tok->astParent(), ". %name% (") && uninitderef) &&
1557 isVariableChanged(tok, v->indirect, mSettings, mTokenizer->isCPP()))
1558 continue;
1559 bool inconclusive = false;
1560 if (isVariableChangedByFunctionCall(tok, v->indirect, mSettings, &inconclusive) || inconclusive)
1561 continue;
1562 uninitvarError(tok, tok->expressionString(), v->errorPath);
1563 const Token* nextTok = nextAfterAstRightmostLeaf(parent);
1564 if (nextTok == scope->bodyEnd)
1565 break;
1566 tok = nextTok ? nextTok : tok;
1567 }
1568 }
1569 }
1570
toString() const1571 std::string CheckUninitVar::MyFileInfo::toString() const
1572 {
1573 return CTU::toString(unsafeUsage);
1574 }
1575
getFileInfo(const Tokenizer * tokenizer,const Settings * settings) const1576 Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const
1577 {
1578 const CheckUninitVar checker(tokenizer, settings, nullptr);
1579 return checker.getFileInfo();
1580 }
1581
isVariableUsage(const Check * check,const Token * vartok,MathLib::bigint * value)1582 static bool isVariableUsage(const Check *check, const Token *vartok, MathLib::bigint *value)
1583 {
1584 (void)value;
1585 const CheckUninitVar *c = dynamic_cast<const CheckUninitVar *>(check);
1586 return c && c->isVariableUsage(vartok, true, CheckUninitVar::Alloc::ARRAY);
1587 }
1588
getFileInfo() const1589 Check::FileInfo *CheckUninitVar::getFileInfo() const
1590 {
1591 const std::list<CTU::FileInfo::UnsafeUsage> &unsafeUsage = CTU::getUnsafeUsage(mTokenizer, mSettings, this, ::isVariableUsage);
1592 if (unsafeUsage.empty())
1593 return nullptr;
1594
1595 MyFileInfo *fileInfo = new MyFileInfo;
1596 fileInfo->unsafeUsage = unsafeUsage;
1597 return fileInfo;
1598 }
1599
loadFileInfoFromXml(const tinyxml2::XMLElement * xmlElement) const1600 Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
1601 {
1602 const std::list<CTU::FileInfo::UnsafeUsage> &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement);
1603 if (unsafeUsage.empty())
1604 return nullptr;
1605
1606 MyFileInfo *fileInfo = new MyFileInfo;
1607 fileInfo->unsafeUsage = unsafeUsage;
1608 return fileInfo;
1609 }
1610
analyseWholeProgram(const CTU::FileInfo * ctu,const std::list<Check::FileInfo * > & fileInfo,const Settings & settings,ErrorLogger & errorLogger)1611 bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
1612 {
1613 if (!ctu)
1614 return false;
1615 bool foundErrors = false;
1616 (void)settings; // This argument is unused
1617
1618 const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> callsMap = ctu->getCallsMap();
1619
1620 for (Check::FileInfo *fi1 : fileInfo) {
1621 const MyFileInfo *fi = dynamic_cast<MyFileInfo*>(fi1);
1622 if (!fi)
1623 continue;
1624 for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) {
1625 const CTU::FileInfo::FunctionCall *functionCall = nullptr;
1626
1627 const std::list<ErrorMessage::FileLocation> &locationList =
1628 CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::uninit,
1629 unsafeUsage,
1630 callsMap,
1631 "Using argument ARG",
1632 &functionCall,
1633 false);
1634 if (locationList.empty())
1635 continue;
1636
1637 const ErrorMessage errmsg(locationList,
1638 emptyString,
1639 Severity::error,
1640 "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression,
1641 "ctuuninitvar",
1642 CWE_USE_OF_UNINITIALIZED_VARIABLE,
1643 Certainty::normal);
1644 errorLogger.reportErr(errmsg);
1645
1646 foundErrors = true;
1647 }
1648 }
1649 return foundErrors;
1650 }
1651