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