Cppcheck
bughuntingchecks.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2022 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 #include "bughuntingchecks.h"
20 
21 #include "astutils.h"
22 #include "errorlogger.h"
23 #include "errortypes.h"
24 #include "library.h"
25 #include "mathlib.h"
26 #include "settings.h"
27 #include "symboldatabase.h"
28 #include "token.h"
29 #include "utils.h"
30 #include "valueflow.h"
31 
32 #include <algorithm>
33 #include <cstring>
34 #include <list>
35 #include <map>
36 #include <memory>
37 #include <string>
38 #include <utility>
39 
40 static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer
41 static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer
42 
43 
44 static float getKnownFloatValue(const Token *tok, float def)
45 {
46  for (const auto &value: tok->values()) {
47  if (value.isKnown() && value.valueType == ValueFlow::Value::ValueType::FLOAT)
48  return value.floatValue;
49  }
50  return def;
51 }
52 
54 {
55  return ExprEngine::BinOpResult("<", lhs, rhs).isTrue(dataBase);
56 }
57 
58 static void arrayIndex(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
59 {
60  if (!Token::simpleMatch(tok->astParent(), "["))
61  return;
62  int nr = 0;
63  const Token *buf = tok->astParent()->astOperand1();
64  while (Token::simpleMatch(buf, "[")) {
65  ++nr;
66  buf = buf->astOperand1();
67  }
68  if (!buf || !buf->variable() || !buf->variable()->isArray() || buf == buf->variable()->nameToken())
69  // TODO
70  return;
71  const Token *index = tok->astParent()->astOperand2();
72  if (tok != index)
73  // TODO
74  return;
75  if (buf->variable()->dimensions().size() > nr && buf->variable()->dimensions()[nr].known) {
76  const MathLib::bigint bufSize = buf->variable()->dimensions()[nr].num;
77  if (value.isGreaterThan(dataBase, bufSize - 1)) {
78  const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue);
79  dataBase->reportError(tok,
80  Severity::SeverityType::error,
81  "bughuntingArrayIndexOutOfBounds",
82  "Array index out of bounds, cannot determine that " + index->expressionString() + " is less than " + std::to_string(bufSize),
84  false,
85  bailout);
86  }
87  }
88  bool isUnsigned = tok->valueType() && tok->valueType()->sign == ::ValueType::Sign::UNSIGNED;
89  if (!isUnsigned && value.isLessThan(dataBase, 0)) {
90  const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue);
91  dataBase->reportError(tok,
92  Severity::SeverityType::error,
93  "bughuntingArrayIndexNegative",
94  "Array index out of bounds, cannot determine that " + index->expressionString() + " is not negative",
96  false,
97  bailout);
98  }
99 }
100 
101 static void bufferOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
102 {
104  return;
105  if (!Token::simpleMatch(tok, "(") || !Token::Match(tok->previous(), "%name% ("))
106  return;
107 
108  const Library::Function *func = dataBase->settings->library.getFunction(tok->previous());
109  if (!func)
110  return;
111 
112  const ExprEngine::FunctionCallArgumentValues *functionCallArguments = dynamic_cast<const ExprEngine::FunctionCallArgumentValues *>(&value);
113  if (!functionCallArguments)
114  return;
115 
116  const std::vector<const Token *> arguments = getArguments(tok);
117  if (functionCallArguments->argValues.size() != arguments.size())
118  // TODO investigate what to do
119  return;
120 
121  int overflowArgument = 0;
122  bool bailout = false;
123 
124  for (const auto &argNrChecks: func->argumentChecks) {
125  const int argnr = argNrChecks.first;
126  const Library::ArgumentChecks &checks = argNrChecks.second;
127  if (argnr <= 0 || argnr > arguments.size() || checks.minsizes.empty())
128  continue;
129 
130  ExprEngine::ValuePtr argValue = functionCallArguments->argValues[argnr - 1];
131  if (!argValue || argValue->type == ExprEngine::ValueType::BailoutValue) {
132  overflowArgument = argnr;
133  bailout = true;
134  break;
135  }
136 
137  std::shared_ptr<ExprEngine::ArrayValue> arrayValue = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(argValue);
138  if (!arrayValue || arrayValue->size.size() != 1) {
139  // TODO: implement this properly.
140  overflowArgument = argnr;
141  bailout = true;
142  break;
143  }
144 
145  for (const Library::ArgumentChecks::MinSize &minsize: checks.minsizes) {
146  if (minsize.type == Library::ArgumentChecks::MinSize::Type::ARGVALUE && minsize.arg > 0 && minsize.arg <= arguments.size()) {
147  ExprEngine::ValuePtr otherValue = functionCallArguments->argValues[minsize.arg - 1];
148  if (!otherValue || otherValue->type == ExprEngine::ValueType::BailoutValue) {
149  overflowArgument = argnr;
150  bailout = true;
151  break;
152  }
153  if (isLessThan(dataBase, arrayValue->size[0], otherValue)) {
154  overflowArgument = argnr;
155  break;
156  }
157  } else if (minsize.type == Library::ArgumentChecks::MinSize::Type::STRLEN && minsize.arg > 0 && minsize.arg <= arguments.size()) {
158  if (func->formatstr) {
159  // TODO: implement this properly. check if minsize refers to a format string and check max length of that..
160  overflowArgument = argnr;
161  bailout = true;
162  break;
163  }
164  if (Token::Match(arguments[minsize.arg - 1], "%str%")) {
165  const Token * const str = arguments[minsize.arg - 1];
166  if (arrayValue->size[0]->isLessThan(dataBase, Token::getStrLength(str))) {
167  overflowArgument = argnr;
168  break;
169  }
170  } else {
171  ExprEngine::ValuePtr otherValue = functionCallArguments->argValues[minsize.arg - 1];
172  if (!otherValue || otherValue->type == ExprEngine::ValueType::BailoutValue) {
173  overflowArgument = argnr;
174  bailout = true;
175  break;
176  }
177  std::shared_ptr<ExprEngine::ArrayValue> arrayValue2 = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(otherValue);
178  if (!arrayValue2 || arrayValue2->size.size() != 1) {
179  overflowArgument = argnr;
180  bailout = true;
181  break;
182  }
183  if (isLessThan(dataBase, arrayValue->size[0], arrayValue2->size[0])) {
184  overflowArgument = argnr;
185  break;
186  }
187  }
188  }
189  }
190 
191  if (overflowArgument > 0)
192  break;
193  }
194 
195  if (overflowArgument == 0)
196  return;
197 
198  dataBase->reportError(tok,
199  Severity::SeverityType::error,
200  "bughuntingBufferOverflow",
201  "Buffer read/write, when calling '" + tok->previous()->str() + "' it cannot be determined that " + std::to_string(overflowArgument) + getOrdinalText(overflowArgument) + " argument is not overflowed",
202  CWE(120),
203  false,
204  bailout);
205 }
206 
207 static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
208 {
209  if (!tok->astParent() || !std::strchr("/%", tok->astParent()->str()[0]))
210  return;
211  if (tok->hasKnownIntValue() && tok->getKnownIntValue() != 0)
212  return;
213  if (tok->isImpossibleIntValue(0))
214  return;
215  if (value.isUninit(dataBase) && value.type != ExprEngine::ValueType::BailoutValue)
216  return;
217  float f = getKnownFloatValue(tok, 0.0f);
218  if (f > 0.0f || f < 0.0f)
219  return;
221  if (Token::simpleMatch(tok->previous(), "sizeof ("))
222  return;
223  }
224  if (tok->astParent()->astOperand2() == tok && value.isEqual(dataBase, 0)) {
225  const char * const id = (tok->valueType() && tok->valueType()->isFloat()) ? "bughuntingDivByZeroFloat" : "bughuntingDivByZero";
226  const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue);
227  dataBase->reportError(dataBase->settings->clang ? tok : tok->astParent(),
228  Severity::SeverityType::error,
229  id,
230  "There is division, cannot determine that there can't be a division by zero.",
231  CWE(369),
232  false,
233  bailout);
234  }
235 }
236 
237 #ifdef BUG_HUNTING_INTEGEROVERFLOW
238 static void integerOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
239 {
240  if (!tok->isArithmeticalOp() || !tok->valueType() || !tok->valueType()->isIntegral() || tok->valueType()->pointer > 0)
241  return;
242 
243  const ExprEngine::BinOpResult *b = dynamic_cast<const ExprEngine::BinOpResult *>(&value);
244  if (!b)
245  return;
246 
247  int bits = getIntBitsFromValueType(tok->valueType(), *dataBase->settings);
248  if (bits == 0 || bits >= 60)
249  return;
250 
251  std::string errorMessage;
252  if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) {
253  MathLib::bigint v = 1LL << (bits - 1);
254  if (b->isGreaterThan(dataBase, v-1))
255  errorMessage = "greater than " + std::to_string(v - 1);
256  if (b->isLessThan(dataBase, -v)) {
257  if (!errorMessage.empty())
258  errorMessage += " or ";
259  errorMessage += "less than " + std::to_string(-v);
260  }
261  } else {
262  MathLib::bigint maxValue = (1LL << bits) - 1;
263  if (b->isGreaterThan(dataBase, maxValue))
264  errorMessage = "greater than " + std::to_string(maxValue);
265  if (b->isLessThan(dataBase, 0)) {
266  if (!errorMessage.empty())
267  errorMessage += " or ";
268  errorMessage += "less than 0";
269  }
270  }
271 
272  if (errorMessage.empty())
273  return;
274 
275 
276  errorMessage = "There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + ").";
277 
278  if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED)
279  errorMessage += " Note that unsigned integer overflow is defined and will wrap around.";
280 
281  dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingIntegerOverflow", errorMessage, false, value.type == ExprEngine::ValueType::BailoutValue);
282 }
283 #endif
284 
285 /** check if variable is unconditionally assigned */
286 static bool isVariableAssigned(const Variable *var, const Token *tok, const Token *scopeStart=nullptr)
287 {
288  const Token * const start = scopeStart && precedes(var->nameToken(), scopeStart) ? scopeStart : var->nameToken();
289 
290  for (const Token *prev = tok->previous(); prev; prev = prev->previous()) {
291  if (!precedes(start, prev))
292  break;
293 
294  if (prev->str() == "}") {
295  if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) {
296  const Token *elseEnd = prev;
297  const Token *elseStart = prev->link();
298  const Token *ifEnd = elseStart->tokAt(-2);
299  const Token *ifStart = ifEnd->link();
300  if (isVariableAssigned(var, ifEnd, ifStart) && isVariableAssigned(var, elseEnd, elseStart)) {
301  return true;
302  }
303  }
304  prev = prev->link();
305  }
306  if (scopeStart && Token::Match(prev, "return|throw|continue|break"))
307  return true;
308  if (Token::Match(prev, "%varid% =", var->declarationId())) {
309  bool usedInRhs = false;
310  visitAstNodes(prev->next()->astOperand2(), [&usedInRhs, var](const Token *tok) {
311  if (tok->varId() == var->declarationId()) {
312  usedInRhs = true;
313  return ChildrenToVisit::done;
314  }
316  });
317  if (!usedInRhs)
318  return true;
319  }
320  }
321  return false;
322 }
323 
324 static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
325 {
326  if (!tok->astParent())
327  return;
328 
329  std::string uninitStructMember;
330  if (const auto* structValue = dynamic_cast<const ExprEngine::StructValue*>(&value)) {
331  uninitStructMember = structValue->getUninitStructMember(dataBase);
332 
333  // uninitialized struct member => is there data copy of struct..
334  if (!uninitStructMember.empty()) {
335  if (!Token::Match(tok->astParent(), "[=,(]"))
336  return;
337  }
338  }
339 
340  bool uninitData = false;
341  if (!value.isUninit(dataBase) && uninitStructMember.empty()) {
342  if (Token::Match(tok->astParent(), "[(,]")) {
343  if (const auto* arrayValue = dynamic_cast<const ExprEngine::ArrayValue*>(&value)) {
344  uninitData = arrayValue->data.size() >= 1 && arrayValue->data[0].value->isUninit(dataBase);
345  }
346  }
347 
348  if (!uninitData)
349  return;
350  }
351 
352  // container is not uninitialized
353  if (tok->valueType() && tok->valueType()->pointer==0 && tok->valueType()->container)
354  return;
355 
356  // container element is not uninitialized
357  if (tok->str() == "[" &&
358  tok->astOperand1() &&
359  tok->astOperand1()->valueType() &&
360  tok->astOperand1()->valueType()->pointer==0 &&
361  tok->astOperand1()->valueType()->container) {
362  if (tok->astOperand1()->valueType()->container->stdStringLike)
363  return;
364  bool pointerType = false;
365  for (const Token *typeTok = tok->astOperand1()->valueType()->containerTypeToken; Token::Match(typeTok, "%name%|*|::|<"); typeTok = typeTok->next()) {
366  if (typeTok->str() == "<" && typeTok->link())
367  typeTok = typeTok->link();
368  if (typeTok->str() == "*")
369  pointerType = true;
370  }
371  if (!pointerType)
372  return;
373  }
374 
375  // variable that is not uninitialized..
376  if (tok->variable() && !tok->variable()->isPointer() && !tok->variable()->isReference()) {
377  // smart pointer is not uninitialized
378  if (tok->variable()->isSmartPointer())
379  return;
380 
381  // struct
382  if (tok->variable()->type() && tok->variable()->type()->needInitialization == Type::NeedInitialization::False)
383  return;
384 
385  // template variable is not uninitialized
386  if (Token::findmatch(tok->variable()->typeStartToken(), "%name% <", tok->variable()->typeEndToken()))
387  return;
388  }
389 
390  // lhs in assignment
391  if (tok->astParent()->str() == "=" && tok == tok->astParent()->astOperand1())
392  return;
393 
394  // Avoid FP when there is bailout..
396  if (tok->hasKnownValue())
397  return;
398  if (!tok->variable())
399  // FIXME
400  return;
401 
402  // lhs for scope operator
403  if (Token::Match(tok, "%name% ::"))
404  return;
405  if (tok->astParent()->str() == "::" && tok == tok->astParent()->astOperand1())
406  return;
407 
408  // Object allocated on the stack
409  if (Token::Match(tok, "%var% .") && tok->next()->originalName() != "->")
410  return;
411 
412  // Assume that stream object is initialized
413  if (Token::Match(tok->previous(), "[;{}] %var% <<|>>") && !tok->next()->astParent())
414  return;
415 
416  // Containers are not uninitialized
417  std::vector<const Token *> tokens{tok, tok->astOperand1(), tok->astOperand2()};
418  if (Token::Match(tok->previous(), ". %name%"))
419  tokens.push_back(tok->previous()->astOperand1());
420  for (const Token *t: tokens) {
421  if (t && t->valueType() && t->valueType()->pointer == 0 && t->valueType()->container)
422  return;
423  }
424 
425  const Variable *var = tok->variable();
426  if (var && var->nameToken() == tok)
427  return;
428  if (var && !var->isLocal())
429  return; // FIXME
430  if (var && !var->isPointer()) {
431  if (!var->isLocal() || var->isStatic())
432  return;
433  }
434  if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || var->isInit()))
435  return;
436  if (var && var->nameToken() == tok)
437  return;
438 
439  // Are there unconditional assignment?
440  if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId()))
441  return;
442 
443  // Arrays are allocated on the stack
444  if (var && Token::Match(tok, "%var% [") && var->isArray())
445  return;
446 
447  if (tok->variable() && isVariableAssigned(tok->variable(), tok))
448  return;
449  }
450 
451  // Uninitialized function argument
452  bool inconclusive = false;
453  if (Token::Match(tok->astParent(), "[,(]")) {
454  const Token *parent = tok->astParent();
455  int count = 0;
456  if (Token::simpleMatch(parent, ",")) {
457  if (tok == parent->astOperand2())
458  count = 1;
459  parent = parent->astParent();
460  while (Token::simpleMatch(parent, ",")) {
461  count++;
462  parent = parent->astParent();
463  }
464  }
465  if (Token::simpleMatch(parent, "(") && parent->astOperand1() != tok) {
466  if (parent->astOperand1()->function()) {
467  const Variable *argvar = parent->astOperand1()->function()->getArgumentVar(count);
468  if (argvar && argvar->isReference() && !argvar->isConst())
469  return;
470  if (uninitData && argvar && !argvar->isConst()) {
471  if (parent->astOperand1()->function()->hasBody())
472  return;
473  inconclusive = true;
474  }
475  if (!uninitStructMember.empty() && dataBase->isC() && argvar && !argvar->isConst()) {
476  if (parent->astOperand1()->function()->hasBody())
477  return;
478  inconclusive = true;
479  }
480  } else if (uninitData) {
481  if (dataBase->settings->library.getFunction(parent->astOperand1()))
482  return;
483  if (parent->astOperand1()->isKeyword())
484  return;
485  }
486  } else if (uninitData)
487  return;
488  }
489 
490  if (inconclusive && !dataBase->settings->certainty.isEnabled(Certainty::inconclusive))
491  return;
492 
493  // Avoid FP for array declaration
494  const Token *parent = tok->astParent();
495  while (parent && parent->str() == "[")
496  parent = parent->astParent();
497  if (!parent)
498  return;
499 
500  const std::string inconclusiveMessage(inconclusive ? ". It is inconclusive if there would be a problem in the function call." : "");
501 
502  if (!uninitStructMember.empty()) {
503  const std::string symbol = tok->expressionString() + "." + uninitStructMember;
504  dataBase->reportError(tok,
505  Severity::SeverityType::error,
506  "bughuntingUninitStructMember",
507  "$symbol:" + symbol + "\nCannot determine that '$symbol' is initialized" + inconclusiveMessage,
509  inconclusive,
511  return;
512  }
513 
514  std::string uninitexpr = tok->expressionString();
515  if (uninitData)
516  uninitexpr += "[0]";
517 
518  const std::string symbol = (tok->varId() > 0) ? ("$symbol:" + tok->str() + "\n") : std::string();
519 
520  std::string constMessage;
521  std::string errorId = "bughuntingUninit";
522 
523  {
524  const Token *vartok = tok;
525  while (Token::Match(vartok, ".|["))
526  vartok = vartok->astOperand1();
527  const Variable *var = vartok ? vartok->variable() : nullptr;
528  if (var && var->isArgument()) {
529  errorId += "NonConstArg";
530  constMessage = " (you can use 'const' to say data must be initialized)";
531  }
532  }
533 
534 
535  dataBase->reportError(tok,
536  Severity::SeverityType::error,
537  errorId.c_str(),
538  symbol + "Cannot determine that '" + uninitexpr + "' is initialized" + constMessage + inconclusiveMessage,
540  inconclusive,
542 }
543 
544 static void checkFunctionCall(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
545 {
546  if (!Token::Match(tok->astParent(), "[(,]"))
547  return;
548  const Token *parent = tok->astParent();
549  while (Token::simpleMatch(parent, ","))
550  parent = parent->astParent();
551  if (!parent || parent->str() != "(")
552  return;
553 
554  int num = 0;
555  for (const Token *argTok: getArguments(parent->astOperand1())) {
556  --num;
557  if (argTok == tok) {
558  num = -num;
559  break;
560  }
561  }
562  if (num <= 0)
563  return;
564 
565  if (parent->astOperand1()->function()) {
566  const Variable *arg = parent->astOperand1()->function()->getArgumentVar(num - 1);
567  if (arg && arg->nameToken()) {
568  std::string bad;
569 
570  MathLib::bigint low;
571  if (arg->nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low)) {
572  if (!(tok->hasKnownIntValue() && tok->getKnownIntValue() >= low) && value.isLessThan(dataBase, low))
573  bad = "__cppcheck_low__(" + std::to_string(low) + ")";
574  }
575 
576  MathLib::bigint high;
577  if (arg->nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) {
578  if (!(tok->hasKnownIntValue() && tok->getKnownIntValue() <= high) && value.isGreaterThan(dataBase, high))
579  bad = "__cppcheck_high__(" + std::to_string(high) + ")";
580  }
581 
582  if (!bad.empty()) {
583  dataBase->reportError(tok,
584  Severity::SeverityType::error,
585  "bughuntingInvalidArgValue",
586  "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument value meets the attribute " + bad,
587  CWE(0),
588  false);
589  return;
590  }
591  }
592  }
593 
594  // Check invalid function argument values..
595  for (const Library::InvalidArgValue &invalidArgValue : Library::getInvalidArgValues(dataBase->settings->library.validarg(parent->astOperand1(), num))) {
596  bool err = false;
597  std::string bad;
598  switch (invalidArgValue.type) {
600  if (!tok->hasKnownIntValue() || tok->getKnownIntValue() == MathLib::toLongNumber(invalidArgValue.op1))
601  err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
602  bad = "equals " + invalidArgValue.op1;
603  break;
605  if (!tok->hasKnownIntValue() || tok->getKnownIntValue() <= MathLib::toLongNumber(invalidArgValue.op1))
606  err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) + 1);
607  bad = "less equal " + invalidArgValue.op1;
608  break;
610  if (!tok->hasKnownIntValue() || tok->getKnownIntValue() < MathLib::toLongNumber(invalidArgValue.op1))
611  err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
612  bad = "less than " + invalidArgValue.op1;
613  break;
615  if (!tok->hasKnownIntValue() || tok->getKnownIntValue() >= MathLib::toLongNumber(invalidArgValue.op1))
616  err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) - 1);
617  bad = "greater equal " + invalidArgValue.op1;
618  break;
620  if (!tok->hasKnownIntValue() || tok->getKnownIntValue() > MathLib::toLongNumber(invalidArgValue.op1))
621  err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
622  bad = "greater than " + invalidArgValue.op1;
623  break;
625  // TODO
626  err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
627  err |= value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op2));
628  bad = "range " + invalidArgValue.op1 + "-" + invalidArgValue.op2;
629  break;
630  }
631 
632  if (err) {
633  dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingInvalidArgValue", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument value is valid. Bad value: " + bad, CWE(0), false);
634  break;
635  }
636  }
637 
638  // Uninitialized function argument..
639  if (dataBase->settings->library.isuninitargbad(parent->astOperand1(), num) && dataBase->settings->library.isnullargbad(parent->astOperand1(), num) && value.type == ExprEngine::ValueType::ArrayValue) {
640  const ExprEngine::ArrayValue &arrayValue = static_cast<const ExprEngine::ArrayValue &>(value);
641  auto index0 = std::make_shared<ExprEngine::IntRange>("0", 0, 0);
642  for (const auto &v: arrayValue.read(index0)) {
643  if (v.second->isUninit(dataBase)) {
644  dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingUninitArg", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument is initialized.", CWE_USE_OF_UNINITIALIZED_VARIABLE, false);
645  break;
646  }
647  }
648  }
649 }
650 
651 static void checkAssignment(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
652 {
653  if (!Token::simpleMatch(tok->astParent(), "="))
654  return;
655  const Token *lhs = tok->astParent()->astOperand1();
656  while (Token::simpleMatch(lhs, "."))
657  lhs = lhs->astOperand2();
658  if (!lhs || !lhs->variable() || !lhs->variable()->nameToken())
659  return;
660 
661  const Token *vartok = lhs->variable()->nameToken();
662 
663  bool executable = false;
664  std::string fullName = lhs->variable()->name();
665  for (const Scope *s = lhs->variable()->nameToken()->scope(); s->type != Scope::ScopeType::eGlobal; s = s->nestedIn) {
666  if (s->isExecutable()) {
667  executable = true;
668  break;
669  }
670  fullName = s->className + "::" + fullName;
671  }
672 
673  auto getMinMaxValue = [=](TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *val) {
674  if (vartok->getCppcheckAttribute(type, val))
675  return true;
676  if (!executable) {
677  const auto it = dataBase->settings->variableContracts.find(fullName);
678  if (it != dataBase->settings->variableContracts.end()) {
679  const std::string *str;
680  if (type == TokenImpl::CppcheckAttributes::Type::LOW)
681  str = &it->second.minValue;
682  else if (type == TokenImpl::CppcheckAttributes::Type::HIGH)
683  str = &it->second.maxValue;
684  else
685  return false;
686  *val = MathLib::toLongNumber(*str);
687  return !str->empty();
688  }
689  }
690  return false;
691  };
692 
693  MathLib::bigint low;
694  if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::LOW, &low)) {
695  if (value.isLessThan(dataBase, low))
696  dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is greater or equal with " + std::to_string(low), CWE_INCORRECT_CALCULATION, false);
697  }
698 
699  MathLib::bigint high;
700  if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) {
701  if (value.isGreaterThan(dataBase, high))
702  dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is lower or equal with " + std::to_string(high), CWE_INCORRECT_CALCULATION, false);
703  }
704 }
705 
706 void addBughuntingChecks(std::vector<ExprEngine::Callback> *callbacks)
707 {
708  callbacks->push_back(arrayIndex);
709  callbacks->push_back(bufferOverflow);
710  callbacks->push_back(divByZero);
711  callbacks->push_back(checkFunctionCall);
712  callbacks->push_back(checkAssignment);
713 #ifdef BUG_HUNTING_INTEGEROVERFLOW
714  callbacks->push_back(integerOverflow);
715 #endif
716  callbacks->push_back(uninit);
717 
718 }
719 
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:792
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:2581
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:53
static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
static void checkAssignment(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
static float getKnownFloatValue(const Token *tok, float def)
static void bufferOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
static const CWE CWE_BUFFER_UNDERRUN(786U)
static const CWE CWE_BUFFER_OVERRUN(788U)
void addBughuntingChecks(std::vector< ExprEngine::Callback > *callbacks)
static bool isVariableAssigned(const Variable *var, const Token *tok, const Token *scopeStart=nullptr)
check if variable is unconditionally assigned
static bool isLessThan(ExprEngine::DataBase *dataBase, ExprEngine::ValuePtr lhs, ExprEngine::ValuePtr rhs)
static void arrayIndex(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
static void checkFunctionCall(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
@ inconclusive
Definition: errortypes.h:47
ConditionalValue::Vector read(ValuePtr index) const
Definition: exprengine.cpp:992
bool isTrue(const DataBase *dataBase) const
virtual bool isLessThan(const DataBase *dataBase, int value) const override
bool isGreaterThan(const DataBase *dataBase, int value) const override
virtual void reportError(const Token *tok, Severity::SeverityType severity, const char id[], const std::string &text, CWE cwe, bool inconclusive, bool incomplete=false, const std::string &functionName=std::string())=0
virtual bool isC() const =0
const Settings *const settings
Definition: exprengine.h:83
const std::vector< ExprEngine::ValuePtr > argValues
Definition: exprengine.h:323
ValueType type
Definition: exprengine.h:128
virtual bool isGreaterThan(const DataBase *dataBase, int value) const
Definition: exprengine.h:112
virtual bool isEqual(const DataBase *dataBase, int value) const
Definition: exprengine.h:107
virtual bool isUninit(const DataBase *dataBase) const
Definition: exprengine.h:122
virtual bool isLessThan(const DataBase *dataBase, int value) const
Definition: exprengine.h:117
std::vector< MinSize > minsizes
Definition: library.h:332
bool isuninitargbad(const Token *ftok, int argnr, int indirect=0, bool *hasIndirect=nullptr) const
Definition: library.cpp:1062
const std::string & validarg(const Token *ftok, int argnr) const
Definition: library.h:399
bool isnullargbad(const Token *ftok, int argnr) const
Definition: library.cpp:1049
static std::vector< InvalidArgValue > getInvalidArgValues(const std::string &validExpr)
Definition: library.cpp:895
const Function * getFunction(const Token *ftok) const
Definition: library.cpp:1397
static bigint toLongNumber(const std::string &str)
Definition: mathlib.cpp:361
long long bigint
Definition: mathlib.h:69
Library library
Library.
Definition: settings.h:235
SimpleEnableGroup< Certainty::CertaintyLevel > certainty
Definition: settings.h:345
bool clang
Use Clang.
Definition: settings.h:145
std::map< std::string, VariableContracts > variableContracts
Definition: settings.h:213
bool isEnabled(T flag) const
Definition: settings.h:68
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:172
void str(T &&s)
Definition: token.h:198
bool hasKnownValue() const
Definition: token.cpp:2378
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:639
bool isImpossibleIntValue(const MathLib::bigint val) const
Definition: token.cpp:2412
const std::string & originalName() const
Definition: token.h:1144
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1000
bool hasKnownIntValue() const
Definition: token.cpp:2369
MathLib::bigint getKnownIntValue() const
Definition: token.h:1169
Token * next() const
Definition: token.h:808
bool isArithmeticalOp() const
Definition: token.h:425
static nonneg int getStrLength(const Token *tok)
Definition: token.cpp:726
const ValueType * valueType() const
Definition: token.h:364
void astOperand1(Token *tok)
Definition: token.cpp:1375
nonneg int varId() const
Definition: token.h:842
std::string expressionString() const
Definition: token.cpp:1560
Token * previous() const
Definition: token.h:837
const Token * tokAt(int index) const
Definition: token.cpp:361
void astOperand2(Token *tok)
Definition: token.cpp:1387
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:975
bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const
Definition: token.h:565
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1026
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1148
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:275
void astParent(Token *tok)
Definition: token.cpp:1356
bool isFloat() const
const Library::Container * container
If the type is a container defined in a cfg file, this is the used.
bool isIntegral() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
enum ValueType::Sign sign
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isReference() const
Is reference variable.
bool isLocal() const
Is variable local.
bool isConst() const
Is variable const.
bool isArray() const
Is variable an array.
const Token * nameToken() const
Get name token.
nonneg int declarationId() const
Get declaration ID (varId used for variable in its declaration).
bool isInit() const
Is variable initialized in its declaration.
bool isPointer() const
Is pointer variable.
bool isStatic() const
Is variable static.
static const struct CWE CWE_USE_OF_UNINITIALIZED_VARIABLE(457U)
CWE id (Common Weakness Enumeration) See https://cwe.mitre.org/ for further reference.
static const struct CWE CWE_INCORRECT_CALCULATION(682U)
static std::string str(ExprEngine::ValuePtr val)
Definition: exprengine.cpp:177
static int getIntBitsFromValueType(const ValueType *vt, const cppcheck::Platform &platform)
std::shared_ptr< Value > ValuePtr
Definition: exprengine.h:73
static const char CWE[]
Definition: resultstree.cpp:52
std::map< int, ArgumentChecks > argumentChecks
Definition: library.h:344
static const char * getOrdinalText(int i)
Definition: utils.h:138
#define bailout(tokenlist, errorLogger, tok, what)
Definition: valueflow.cpp:143