Cppcheck
checkbufferoverrun.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 //---------------------------------------------------------------------------
20 // Buffer overrun..
21 //---------------------------------------------------------------------------
22 
23 #include "checkbufferoverrun.h"
24 
25 #include "astutils.h"
26 #include "errorlogger.h"
27 #include "library.h"
28 #include "mathlib.h"
29 #include "platform.h"
30 #include "settings.h"
31 #include "symboldatabase.h"
32 #include "token.h"
33 #include "tokenize.h"
34 #include "utils.h"
35 #include "valueflow.h"
36 
37 #include <algorithm>
38 #include <cstdlib>
39 #include <functional>
40 #include <iterator>
41 #include <numeric> // std::accumulate
42 #include <sstream>
43 #include <utility>
44 
45 #include "xml.h"
46 
47 //---------------------------------------------------------------------------
48 
49 // Register this check class (by creating a static instance of it)
50 namespace {
51  CheckBufferOverrun instance;
52 }
53 
54 //---------------------------------------------------------------------------
55 
56 // CWE ids used:
57 static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size
58 static const CWE CWE170(170U); // Improper Null Termination
59 static const CWE CWE_ARGUMENT_SIZE(398U); // Indicator of Poor Code Quality
60 static const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U); // Indicator of Poor Code Quality
61 static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
62 static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
63 static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer
64 static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer
65 
66 //---------------------------------------------------------------------------
67 
68 static const ValueFlow::Value *getBufferSizeValue(const Token *tok)
69 {
70  const std::list<ValueFlow::Value> &tokenValues = tok->values();
71  const auto it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue));
72  return it == tokenValues.cend() ? nullptr : &*it;
73 }
74 
75 static int getMinFormatStringOutputLength(const std::vector<const Token*> &parameters, nonneg int formatStringArgNr)
76 {
77  if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size())
78  return 0;
79  if (parameters[formatStringArgNr - 1]->tokType() != Token::eString)
80  return 0;
81  const std::string &formatString = parameters[formatStringArgNr - 1]->str();
82  bool percentCharFound = false;
83  int outputStringSize = 0;
84  bool handleNextParameter = false;
85  std::string digits_string;
86  bool i_d_x_f_found = false;
87  int parameterLength = 0;
88  int inputArgNr = formatStringArgNr;
89  for (int i = 1; i + 1 < formatString.length(); ++i) {
90  if (formatString[i] == '\\') {
91  if (i < formatString.length() - 1 && formatString[i + 1] == '0')
92  break;
93 
94  ++outputStringSize;
95  ++i;
96  continue;
97  }
98 
99  if (percentCharFound) {
100  switch (formatString[i]) {
101  case 'f':
102  case 'x':
103  case 'X':
104  case 'i':
105  i_d_x_f_found = true;
106  handleNextParameter = true;
107  parameterLength = 1; // TODO
108  break;
109  case 'c':
110  case 'e':
111  case 'E':
112  case 'g':
113  case 'o':
114  case 'u':
115  case 'p':
116  case 'n':
117  handleNextParameter = true;
118  parameterLength = 1; // TODO
119  break;
120  case 'd':
121  i_d_x_f_found = true;
122  parameterLength = 1;
123  if (inputArgNr < parameters.size() && parameters[inputArgNr]->hasKnownIntValue())
124  parameterLength = std::to_string(parameters[inputArgNr]->getKnownIntValue()).length();
125 
126  handleNextParameter = true;
127  break;
128  case 's':
129  parameterLength = 0;
130  if (inputArgNr < parameters.size() && parameters[inputArgNr]->tokType() == Token::eString)
131  parameterLength = Token::getStrLength(parameters[inputArgNr]);
132 
133  handleNextParameter = true;
134  break;
135  }
136  }
137 
138  if (formatString[i] == '%')
139  percentCharFound = !percentCharFound;
140  else if (percentCharFound) {
141  digits_string.append(1, formatString[i]);
142  }
143 
144  if (!percentCharFound)
145  outputStringSize++;
146 
147  if (handleNextParameter) {
148  // NOLINTNEXTLINE(cert-err34-c) - intentional use
149  int tempDigits = std::abs(std::atoi(digits_string.c_str()));
150  if (i_d_x_f_found)
151  tempDigits = std::max(tempDigits, 1);
152 
153  if (digits_string.find('.') != std::string::npos) {
154  const std::string endStr = digits_string.substr(digits_string.find('.') + 1);
155  // NOLINTNEXTLINE(cert-err34-c) - intentional use
156  const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1);
157 
158  if (formatString[i] == 's') {
159  // For strings, the length after the dot "%.2s" will limit
160  // the length of the string.
161  if (parameterLength > maxLen)
162  parameterLength = maxLen;
163  } else {
164  // For integers, the length after the dot "%.2d" can
165  // increase required length
166  if (tempDigits < maxLen)
167  tempDigits = maxLen;
168  }
169  }
170 
171  if (tempDigits < parameterLength)
172  outputStringSize += parameterLength;
173  else
174  outputStringSize += tempDigits;
175 
176  parameterLength = 0;
177  digits_string.clear();
178  i_d_x_f_found = false;
179  percentCharFound = false;
180  handleNextParameter = false;
181  ++inputArgNr;
182  }
183  }
184 
185  return outputStringSize;
186 }
187 
188 //---------------------------------------------------------------------------
189 
190 static bool getDimensionsEtc(const Token * const arrayToken, const Settings &settings, std::vector<Dimension> &dimensions, ErrorPath &errorPath, bool &mightBeLarger, MathLib::bigint &path)
191 {
192  const Token *array = arrayToken;
193  while (Token::Match(array, ".|::"))
194  array = array->astOperand2();
195 
196  if (array->variable() && array->variable()->isArray() && !array->variable()->dimensions().empty()) {
197  dimensions = array->variable()->dimensions();
198  if (dimensions[0].num <= 1 || !dimensions[0].tok) {
199  visitAstNodes(arrayToken,
200  [&](const Token *child) {
201  if (child->originalName() == "->") {
202  mightBeLarger = true;
203  return ChildrenToVisit::none;
204  }
206  });
207  }
208  } else if (const Token *stringLiteral = array->getValueTokenMinStrSize(settings, &path)) {
209  Dimension dim;
210  dim.tok = nullptr;
211  dim.num = Token::getStrArraySize(stringLiteral);
212  dim.known = array->hasKnownValue();
213  dimensions.emplace_back(dim);
214  } else if (array->valueType() && array->valueType()->pointer >= 1 && (array->valueType()->isIntegral() || array->valueType()->isFloat())) {
215  const ValueFlow::Value *value = getBufferSizeValue(array);
216  if (!value)
217  return false;
218  path = value->path;
219  errorPath = value->errorPath;
220  Dimension dim;
221  dim.known = value->isKnown();
222  dim.tok = nullptr;
223  const int typeSize = array->valueType()->typeSize(settings.platform, array->valueType()->pointer > 1);
224  if (typeSize == 0)
225  return false;
226  dim.num = value->intvalue / typeSize;
227  dimensions.emplace_back(dim);
228  }
229  return !dimensions.empty();
230 }
231 
233 {
234  ValueFlow::Value v(size);
235  v.path = path;
236  return v;
237 }
238 
239 static std::vector<ValueFlow::Value> getOverrunIndexValues(const Token* tok,
240  const Token* arrayToken,
241  const std::vector<Dimension>& dimensions,
242  const std::vector<const Token*>& indexTokens,
243  MathLib::bigint path)
244 {
245  const Token *array = arrayToken;
246  while (Token::Match(array, ".|::"))
247  array = array->astOperand2();
248 
249  bool isArrayIndex = tok->str() == "[";
250  if (isArrayIndex) {
251  const Token* parent = tok;
252  while (Token::simpleMatch(parent, "["))
253  parent = parent->astParent();
254  if (!parent || parent->isUnaryOp("&"))
255  isArrayIndex = false;
256  }
257 
258  bool overflow = false;
259  std::vector<ValueFlow::Value> indexValues;
260  for (int i = 0; i < dimensions.size() && i < indexTokens.size(); ++i) {
261  MathLib::bigint size = dimensions[i].num;
262  if (!isArrayIndex)
263  size++;
264  const bool zeroArray = array->variable() && array->variable()->isArray() && dimensions[i].num == 0;
265  std::vector<ValueFlow::Value> values = !zeroArray
266  ? ValueFlow::isOutOfBounds(makeSizeValue(size, path), indexTokens[i])
267  : std::vector<ValueFlow::Value>{};
268  if (values.empty()) {
269  if (indexTokens[i]->hasKnownIntValue())
270  indexValues.push_back(indexTokens[i]->values().front());
271  else
272  indexValues.push_back(ValueFlow::Value::unknown());
273  continue;
274  }
275  overflow = true;
276  indexValues.push_back(values.front());
277  }
278  if (overflow)
279  return indexValues;
280  return {};
281 }
282 
284 {
285  logChecker("CheckBufferOverrun::arrayIndex");
286 
287  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
288  if (tok->str() != "[")
289  continue;
290  const Token *array = tok->astOperand1();
291  while (Token::Match(array, ".|::"))
292  array = array->astOperand2();
293  if (!array || ((!array->variable() || array->variable()->nameToken() == array) && array->tokType() != Token::eString))
294  continue;
295  if (!array->scope()->isExecutable()) {
296  // LHS in non-executable scope => This is just a definition
297  const Token *parent = tok;
298  while (parent && !Token::simpleMatch(parent->astParent(), "="))
299  parent = parent->astParent();
300  if (!parent || parent == parent->astParent()->astOperand1())
301  continue;
302  }
303 
304  if (astIsContainer(array))
305  continue;
306 
307  std::vector<const Token *> indexTokens;
308  for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) {
309  if (!tok2->astOperand2()) {
310  indexTokens.clear();
311  break;
312  }
313  indexTokens.emplace_back(tok2->astOperand2());
314  }
315  if (indexTokens.empty())
316  continue;
317 
318  std::vector<Dimension> dimensions;
319  ErrorPath errorPath;
320  bool mightBeLarger = false;
321  MathLib::bigint path = 0;
322  if (!getDimensionsEtc(tok->astOperand1(), *mSettings, dimensions, errorPath, mightBeLarger, path))
323  continue;
324 
325  const Variable* const var = array->variable();
326  if (var && var->isArgument() && var->scope()) {
327  const Token* changeTok = var->scope()->bodyStart;
328  bool isChanged = false;
329  while ((changeTok = findVariableChanged(changeTok->next(), var->scope()->bodyEnd, /*indirect*/ 0, var->declarationId(),
330  /*globalvar*/ false, *mSettings))) {
331  if (!Token::simpleMatch(changeTok->astParent(), "[")) {
332  isChanged = true;
333  break;
334  }
335  }
336  if (isChanged)
337  continue;
338  }
339 
340  // Positive index
341  if (!mightBeLarger) { // TODO check arrays with dim 1 also
342  const std::vector<ValueFlow::Value>& indexValues =
343  getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens, path);
344  if (!indexValues.empty())
345  arrayIndexError(tok, dimensions, indexValues);
346  }
347 
348  // Negative index
349  bool neg = false;
350  std::vector<ValueFlow::Value> negativeIndexes;
351  for (const Token * indexToken : indexTokens) {
352  const ValueFlow::Value *negativeValue = indexToken->getValueLE(-1, *mSettings);
353  if (negativeValue) {
354  negativeIndexes.emplace_back(*negativeValue);
355  neg = true;
356  } else {
357  negativeIndexes.emplace_back(ValueFlow::Value::unknown());
358  }
359  }
360  if (neg) {
361  negativeIndexError(tok, dimensions, negativeIndexes);
362  }
363  }
364 }
365 
366 static std::string stringifyIndexes(const std::string& array, const std::vector<ValueFlow::Value>& indexValues)
367 {
368  if (indexValues.size() == 1)
369  return std::to_string(indexValues[0].intvalue);
370 
371  std::ostringstream ret;
372  ret << array;
373  for (const ValueFlow::Value& index : indexValues) {
374  ret << "[";
375  if (index.isNonValue())
376  ret << "*";
377  else
378  ret << index.intvalue;
379  ret << "]";
380  }
381  return ret.str();
382 }
383 
384 static std::string arrayIndexMessage(const Token* tok,
385  const std::vector<Dimension>& dimensions,
386  const std::vector<ValueFlow::Value>& indexValues,
387  const Token* condition)
388 {
389  auto add_dim = [](const std::string &s, const Dimension &dim) {
390  return s + "[" + std::to_string(dim.num) + "]";
391  };
392  const std::string array = std::accumulate(dimensions.cbegin(), dimensions.cend(), tok->astOperand1()->expressionString(), std::move(add_dim));
393 
394  std::ostringstream errmsg;
395  if (condition)
396  errmsg << ValueFlow::eitherTheConditionIsRedundant(condition)
397  << " or the array '" << array << "' is accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds.";
398  else
399  errmsg << "Array '" << array << "' accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds.";
400 
401  return errmsg.str();
402 }
403 
405  const std::vector<Dimension>& dimensions,
406  const std::vector<ValueFlow::Value>& indexes)
407 {
408  if (!tok) {
409  reportError(tok, Severity::error, "arrayIndexOutOfBounds", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal);
410  reportError(tok, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal);
411  return;
412  }
413 
414  const Token *condition = nullptr;
415  const ValueFlow::Value *index = nullptr;
416  for (const ValueFlow::Value& indexValue : indexes) {
417  if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning))
418  return;
419  if (indexValue.condition)
420  condition = indexValue.condition;
421  if (!index || !indexValue.errorPath.empty())
422  index = &indexValue;
423  }
424 
425  reportError(getErrorPath(tok, index, "Array index out of bounds"),
427  index->condition ? "arrayIndexOutOfBoundsCond" : "arrayIndexOutOfBounds",
428  arrayIndexMessage(tok, dimensions, indexes, condition),
431 }
432 
434  const std::vector<Dimension>& dimensions,
435  const std::vector<ValueFlow::Value>& indexes)
436 {
437  if (!tok) {
438  reportError(tok, Severity::error, "negativeIndex", "Negative array index", CWE_BUFFER_UNDERRUN, Certainty::normal);
439  return;
440  }
441 
442  const Token *condition = nullptr;
443  const ValueFlow::Value *negativeValue = nullptr;
444  for (const ValueFlow::Value& indexValue : indexes) {
445  if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning))
446  return;
447  if (indexValue.condition)
448  condition = indexValue.condition;
449  if (!negativeValue || !indexValue.errorPath.empty())
450  negativeValue = &indexValue;
451  }
452 
453  reportError(getErrorPath(tok, negativeValue, "Negative array index"),
454  negativeValue->errorSeverity() ? Severity::error : Severity::warning,
455  "negativeIndex",
456  arrayIndexMessage(tok, dimensions, indexes, condition),
459 }
460 
461 //---------------------------------------------------------------------------
462 
464 {
466  return;
467 
468  logChecker("CheckBufferOverrun::pointerArithmetic"); // portability
469 
470  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
471  if (!Token::Match(tok, "+|-"))
472  continue;
473  if (!tok->valueType() || tok->valueType()->pointer == 0)
474  continue;
475  if (!tok->isBinaryOp())
476  continue;
477  if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType())
478  continue;
479 
480  const Token *arrayToken, *indexToken;
481  if (tok->astOperand1()->valueType()->pointer > 0) {
482  arrayToken = tok->astOperand1();
483  indexToken = tok->astOperand2();
484  } else {
485  arrayToken = tok->astOperand2();
486  indexToken = tok->astOperand1();
487  }
488 
489  if (!indexToken || !indexToken->valueType() || indexToken->valueType()->pointer > 0 || !indexToken->valueType()->isIntegral())
490  continue;
491 
492  std::vector<Dimension> dimensions;
493  ErrorPath errorPath;
494  bool mightBeLarger = false;
495  MathLib::bigint path = 0;
496  if (!getDimensionsEtc(arrayToken, *mSettings, dimensions, errorPath, mightBeLarger, path))
497  continue;
498 
499  if (tok->str() == "+") {
500  // Positive index
501  if (!mightBeLarger) { // TODO check arrays with dim 1 also
502  const std::vector<const Token *> indexTokens{indexToken};
503  const std::vector<ValueFlow::Value>& indexValues =
504  getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens, path);
505  if (!indexValues.empty())
506  pointerArithmeticError(tok, indexToken, &indexValues.front());
507  }
508 
509  if (const ValueFlow::Value *neg = indexToken->getValueLE(-1, *mSettings))
510  pointerArithmeticError(tok, indexToken, neg);
511  } else if (tok->str() == "-") {
512  if (arrayToken->variable() && arrayToken->variable()->isArgument())
513  continue;
514 
515  const Token *array = arrayToken;
516  while (Token::Match(array, ".|::"))
517  array = array->astOperand2();
518  if (array->variable() && array->variable()->isArray()) {
519  const ValueFlow::Value *v = indexToken->getValueGE(1, *mSettings);
520  if (v)
521  pointerArithmeticError(tok, indexToken, v);
522  }
523  }
524  }
525 }
526 
527 void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue)
528 {
529  if (!tok) {
530  reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal);
531  reportError(tok, Severity::portability, "pointerOutOfBoundsCond", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal);
532  return;
533  }
534 
535  std::string errmsg;
536  if (indexValue->condition)
537  errmsg = "Undefined behaviour, when '" + indexToken->expressionString() + "' is " + std::to_string(indexValue->intvalue) + " the pointer arithmetic '" + tok->expressionString() + "' is out of bounds.";
538  else
539  errmsg = "Undefined behaviour, pointer arithmetic '" + tok->expressionString() + "' is out of bounds.";
540 
541  reportError(getErrorPath(tok, indexValue, "Pointer arithmetic overflow"),
543  indexValue->condition ? "pointerOutOfBoundsCond" : "pointerOutOfBounds",
544  errmsg,
547 }
548 
549 //---------------------------------------------------------------------------
550 
552 {
553  if (!bufTok->valueType())
554  return ValueFlow::Value(-1);
555  const Variable *var = bufTok->variable();
556 
557  if (!var || var->dimensions().empty()) {
558  const ValueFlow::Value *value = getBufferSizeValue(bufTok);
559  if (value)
560  return *value;
561  }
562 
563  if (!var)
564  return ValueFlow::Value(-1);
565 
566  const MathLib::bigint dim = std::accumulate(var->dimensions().cbegin(), var->dimensions().cend(), 1LL, [](MathLib::bigint i1, const Dimension &dim) {
567  return i1 * dim.num;
568  });
569 
571  v.setKnown();
573 
574  if (var->isPointerArray())
576  else if (var->isPointer())
577  return ValueFlow::Value(-1);
578  else {
579  const MathLib::bigint typeSize = bufTok->valueType()->typeSize(mSettings->platform);
580  v.intvalue = dim * typeSize;
581  }
582 
583  return v;
584 }
585 //---------------------------------------------------------------------------
586 
587 static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector<const Token *> &args, const MathLib::bigint bufferSize, const Settings &settings, const Tokenizer* tokenizer)
588 {
589  const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr;
590  const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr;
591 
592  switch (minsize.type) {
594  if (settings.library.isargformatstr(ftok, minsize.arg)) {
595  return getMinFormatStringOutputLength(args, minsize.arg) < bufferSize;
596  } else if (arg) {
597  const Token *strtoken = arg->getValueTokenMaxStrLength();
598  if (strtoken)
599  return Token::getStrLength(strtoken) < bufferSize;
600  }
601  break;
603  if (arg && arg->hasKnownIntValue()) {
604  MathLib::bigint myMinsize = arg->getKnownIntValue();
605  const unsigned int baseSize = tokenizer->sizeOfType(minsize.baseType);
606  if (baseSize != 0)
607  myMinsize *= baseSize;
608  return myMinsize <= bufferSize;
609  }
610  break;
611  }
613  // TODO
614  break;
616  if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue())
617  return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize;
618  break;
620  MathLib::bigint myMinsize = minsize.value;
621  const unsigned int baseSize = tokenizer->sizeOfType(minsize.baseType);
622  if (baseSize != 0)
623  myMinsize *= baseSize;
624  return myMinsize <= bufferSize;
625  }
627  break;
628  }
629  return true;
630 }
631 
632 
634 {
635  logChecker("CheckBufferOverrun::bufferOverflow");
636 
637  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
638  for (const Scope * scope : symbolDatabase->functionScopes) {
639  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
640  if (!Token::Match(tok, "%name% (") || Token::simpleMatch(tok, ") {"))
641  continue;
642  if (!mSettings->library.hasminsize(tok))
643  continue;
644  const std::vector<const Token *> args = getArguments(tok);
645  for (int argnr = 0; argnr < args.size(); ++argnr) {
646  if (!args[argnr]->valueType() || args[argnr]->valueType()->pointer == 0)
647  continue;
648  const std::vector<Library::ArgumentChecks::MinSize> *minsizes = mSettings->library.argminsizes(tok, argnr + 1);
649  if (!minsizes || minsizes->empty())
650  continue;
651  // Get buffer size..
652  const Token *argtok = args[argnr];
653  while (argtok && argtok->isCast())
654  argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1();
655  while (Token::Match(argtok, ".|::"))
656  argtok = argtok->astOperand2();
657  if (!argtok || !argtok->variable())
658  continue;
659  if (argtok->valueType() && argtok->valueType()->pointer == 0)
660  continue;
661  // TODO: strcpy(buf+10, "hello");
662  const ValueFlow::Value bufferSize = getBufferSize(argtok);
663  if (bufferSize.intvalue <= 0)
664  continue;
665  // buffer size == 1 => do not warn for dynamic memory
666  if (bufferSize.intvalue == 1 && Token::simpleMatch(argtok->astParent(), ".")) { // TODO: check if parent was allocated dynamically
667  const Token *tok2 = argtok;
668  while (Token::simpleMatch(tok2->astParent(), "."))
669  tok2 = tok2->astParent();
670  while (Token::Match(tok2, "[|."))
671  tok2 = tok2->astOperand1();
672  const Variable *var = tok2 ? tok2->variable() : nullptr;
673  if (var) {
674  if (var->isPointer())
675  continue;
676  if (var->isArgument() && var->isReference())
677  continue;
678  }
679  }
680  const bool error = std::none_of(minsizes->begin(), minsizes->end(), [=](const Library::ArgumentChecks::MinSize &minsize) {
681  return checkBufferSize(tok, minsize, args, bufferSize.intvalue, *mSettings, mTokenizer);
682  });
683  if (error)
684  bufferOverflowError(args[argnr], &bufferSize, Certainty::normal);
685  }
686  }
687  }
688 }
689 
691 {
692  reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty);
693 }
694 
695 //---------------------------------------------------------------------------
696 
698 {
700  return;
701 
702  logChecker("CheckBufferOverrun::arrayIndexThenCheck");
703 
704  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
705  for (const Scope * const scope : symbolDatabase->functionScopes) {
706  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
707  if (Token::simpleMatch(tok, "sizeof (")) {
708  tok = tok->linkAt(1);
709  continue;
710  }
711 
712  if (Token::Match(tok, "%name% [ %var% ]")) {
713  tok = tok->next();
714 
715  const int indexID = tok->next()->varId();
716  const std::string& indexName(tok->strAt(1));
717 
718  // Iterate AST upwards
719  const Token* tok2 = tok;
720  const Token* tok3 = tok2;
721  while (tok2->astParent() && tok2->tokType() != Token::eLogicalOp && tok2->str() != "?") {
722  tok3 = tok2;
723  tok2 = tok2->astParent();
724  }
725 
726  // Ensure that we ended at a logical operator and that we came from its left side
727  if (tok2->tokType() != Token::eLogicalOp || tok2->astOperand1() != tok3)
728  continue;
729 
730  // check if array index is ok
731  // statement can be closed in parentheses, so "(| " is using
732  if (Token::Match(tok2, "&& (| %varid% <|<=", indexID))
733  arrayIndexThenCheckError(tok, indexName);
734  else if (Token::Match(tok2, "&& (| %any% >|>= %varid% !!+", indexID))
735  arrayIndexThenCheckError(tok, indexName);
736  }
737  }
738  }
739 }
740 
741 void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName)
742 {
743  reportError(tok, Severity::style, "arrayIndexThenCheck",
744  "$symbol:" + indexName + "\n"
745  "Array index '$symbol' is used before limits check.\n"
746  "Defensive programming: The variable '$symbol' is used as an array index before it "
747  "is checked that is within limits. This can mean that the array might be accessed out of bounds. "
748  "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will "
749  "not be accessed if the index is out of limits.", CWE_ARRAY_INDEX_THEN_CHECK, Certainty::normal);
750 }
751 
752 //---------------------------------------------------------------------------
753 
755 {
756  // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3
758  return;
759 
760  logChecker("CheckBufferOverrun::stringNotZeroTerminated"); // warning,inconclusive
761 
762  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
763  for (const Scope * const scope : symbolDatabase->functionScopes) {
764  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
765  if (!Token::simpleMatch(tok, "strncpy ("))
766  continue;
767  const std::vector<const Token *> args = getArguments(tok);
768  if (args.size() != 3)
769  continue;
770  const Token *sizeToken = args[2];
771  if (!sizeToken->hasKnownIntValue())
772  continue;
773  const ValueFlow::Value &bufferSize = getBufferSize(args[0]);
774  if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue)
775  continue;
776  if (Token::simpleMatch(args[1], "(") && Token::simpleMatch(args[1]->astOperand1(), ". c_str") && args[1]->astOperand1()->astOperand1()) {
777  const std::list<ValueFlow::Value>& contValues = args[1]->astOperand1()->astOperand1()->values();
778  auto it = std::find_if(contValues.cbegin(), contValues.cend(), [](const ValueFlow::Value& value) {
779  return value.isContainerSizeValue() && !value.isImpossible();
780  });
781  if (it != contValues.end() && it->intvalue < sizeToken->getKnownIntValue())
782  continue;
783  } else {
784  const Token* srcValue = args[1]->getValueTokenMaxStrLength();
785  if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue())
786  continue;
787  }
788  // Is the buffer zero terminated after the call?
789  bool isZeroTerminated = false;
790  for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) {
791  if (!Token::simpleMatch(tok2, "] ="))
792  continue;
793  const Token *rhs = tok2->next()->astOperand2();
794  if (!rhs || !rhs->hasKnownIntValue() || rhs->getKnownIntValue() != 0)
795  continue;
796  if (isSameExpression(false, args[0], tok2->link()->astOperand1(), *mSettings, false, false))
797  isZeroTerminated = true;
798  }
799  if (isZeroTerminated)
800  continue;
801  // TODO: Locate unsafe string usage..
802  terminateStrncpyError(tok, args[0]->expressionString());
803  }
804  }
805 }
806 
807 void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname)
808 {
809  const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy().";
810  reportError(tok, Severity::warning, "terminateStrncpy",
811  "$symbol:" + varname + '\n' +
812  shortMessage + '\n' +
813  shortMessage + ' ' +
814  "If the source string's size fits or exceeds the given size, strncpy() does not add a "
815  "zero at the end of the buffer. This causes bugs later in the code if the code "
816  "assumes buffer is null-terminated.", CWE170, Certainty::inconclusive);
817 }
818 //---------------------------------------------------------------------------
819 
821 {
822  // Check '%type% x[10]' arguments
824  return;
825 
826  logChecker("CheckBufferOverrun::argumentSize"); // warning
827 
828  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
829  for (const Scope * const scope : symbolDatabase->functionScopes) {
830  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
831  if (!tok->function() || !Token::Match(tok, "%name% ("))
832  continue;
833 
834  // If argument is '%type% a[num]' then check bounds against num
835  const Function *callfunc = tok->function();
836  const std::vector<const Token *> callargs = getArguments(tok);
837  for (nonneg int paramIndex = 0; paramIndex < callargs.size() && paramIndex < callfunc->argCount(); ++paramIndex) {
838  const Variable* const argument = callfunc->getArgumentVar(paramIndex);
839  if (!argument || !argument->nameToken() || !argument->isArray())
840  continue;
841  if (!argument->valueType() || !callargs[paramIndex]->valueType())
842  continue;
843  if (argument->valueType()->type != callargs[paramIndex]->valueType()->type)
844  continue;
845  const Token * calldata = callargs[paramIndex];
846  while (Token::Match(calldata, "::|."))
847  calldata = calldata->astOperand2();
848  if (!calldata->variable() || !calldata->variable()->isArray())
849  continue;
850  if (calldata->variable()->dimensions().size() != argument->dimensions().size())
851  continue;
852  bool err = false;
853  for (int d = 0; d < argument->dimensions().size(); ++d) {
854  const auto& dim1 = calldata->variable()->dimensions()[d];
855  const auto& dim2 = argument->dimensions()[d];
856  if (!dim1.known || !dim2.known)
857  break;
858  if (dim1.num < dim2.num)
859  err = true;
860  }
861  if (err)
862  argumentSizeError(tok, tok->str(), paramIndex, callargs[paramIndex]->expressionString(), calldata->variable(), argument);
863  }
864  }
865  }
866 }
867 
868 void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string &paramExpression, const Variable *paramVar, const Variable *functionArg)
869 {
870  const std::string strParamNum = std::to_string(paramIndex + 1) + getOrdinalText(paramIndex + 1);
871  ErrorPath errorPath;
872  errorPath.emplace_back(tok, "Function '" + functionName + "' is called");
873  if (functionArg)
874  errorPath.emplace_back(functionArg->nameToken(), "Declaration of " + strParamNum + " function argument.");
875  if (paramVar)
876  errorPath.emplace_back(paramVar->nameToken(), "Passing buffer '" + paramVar->name() + "' to function that is declared here");
877  errorPath.emplace_back(tok, "");
878 
879  reportError(errorPath, Severity::warning, "argumentSize",
880  "$symbol:" + functionName + '\n' +
881  "Buffer '" + paramExpression + "' is too small, the function '" + functionName + "' expects a bigger buffer in " + strParamNum + " argument", CWE_ARGUMENT_SIZE, Certainty::normal);
882 }
883 
884 //---------------------------------------------------------------------------
885 // CTU..
886 //---------------------------------------------------------------------------
887 
888 // a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now
889 // see https://trac.cppcheck.net/ticket/12108 for more details
890 #ifdef __clang__
891 inline namespace CheckBufferOverrun_internal
892 #else
893 namespace
894 #endif
895 {
896  /** data for multifile checking */
897  class MyFileInfo : public Check::FileInfo {
898  public:
899  /** unsafe array index usage */
900  std::list<CTU::FileInfo::UnsafeUsage> unsafeArrayIndex;
901 
902  /** unsafe pointer arithmetics */
903  std::list<CTU::FileInfo::UnsafeUsage> unsafePointerArith;
904 
905  /** Convert data into xml string */
906  std::string toString() const override
907  {
908  std::string xml;
909  if (!unsafeArrayIndex.empty())
910  xml = " <array-index>\n" + CTU::toString(unsafeArrayIndex) + " </array-index>\n";
911  if (!unsafePointerArith.empty())
912  xml += " <pointer-arith>\n" + CTU::toString(unsafePointerArith) + " </pointer-arith>\n";
913  return xml;
914  }
915  };
916 }
917 
918 bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const Token *argtok, MathLib::bigint *offset, int type)
919 {
920  if (!offset)
921  return false;
922  if (!argtok->valueType() || argtok->valueType()->typeSize(settings.platform) == 0)
923  return false;
924  const Token *indexTok = nullptr;
925  if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] ["))
926  indexTok = argtok->next()->astOperand2();
927  else if (type == 2 && Token::simpleMatch(argtok->astParent(), "+"))
928  indexTok = (argtok == argtok->astParent()->astOperand1()) ?
929  argtok->astParent()->astOperand2() :
930  argtok->astParent()->astOperand1();
931  if (!indexTok)
932  return false;
933  if (!indexTok->hasKnownIntValue())
934  return false;
935  *offset = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(settings.platform);
936  return true;
937 }
938 
939 bool CheckBufferOverrun::isCtuUnsafeArrayIndex(const Settings &settings, const Token *argtok, MathLib::bigint *offset)
940 {
941  return isCtuUnsafeBufferUsage(settings, argtok, offset, 1);
942 }
943 
944 bool CheckBufferOverrun::isCtuUnsafePointerArith(const Settings &settings, const Token *argtok, MathLib::bigint *offset)
945 {
946  return isCtuUnsafeBufferUsage(settings, argtok, offset, 2);
947 }
948 
949 /** @brief Parse current TU and extract file info */
950 Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const
951 {
952  const std::list<CTU::FileInfo::UnsafeUsage> &unsafeArrayIndex = CTU::getUnsafeUsage(tokenizer, settings, isCtuUnsafeArrayIndex);
953  const std::list<CTU::FileInfo::UnsafeUsage> &unsafePointerArith = CTU::getUnsafeUsage(tokenizer, settings, isCtuUnsafePointerArith);
954  if (unsafeArrayIndex.empty() && unsafePointerArith.empty()) {
955  return nullptr;
956  }
957  auto *fileInfo = new MyFileInfo;
958  fileInfo->unsafeArrayIndex = unsafeArrayIndex;
959  fileInfo->unsafePointerArith = unsafePointerArith;
960  return fileInfo;
961 }
962 
963 Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
964 {
965  // cppcheck-suppress shadowFunction - TODO: fix this
966  const std::string arrayIndex("array-index");
967  const std::string pointerArith("pointer-arith");
968 
969  auto *fileInfo = new MyFileInfo;
970  for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {
971  if (e->Name() == arrayIndex)
972  fileInfo->unsafeArrayIndex = CTU::loadUnsafeUsageListFromXml(e);
973  else if (e->Name() == pointerArith)
974  fileInfo->unsafePointerArith = CTU::loadUnsafeUsageListFromXml(e);
975  }
976 
977  if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) {
978  delete fileInfo;
979  return nullptr;
980  }
981 
982  return fileInfo;
983 }
984 
985 /** @brief Analyse all file infos for all TU */
986 bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
987 {
988  if (!ctu)
989  return false;
990  bool foundErrors = false;
991  (void)settings; // This argument is unused
992 
993  CheckBufferOverrun dummy(nullptr, &settings, &errorLogger);
994  dummy.
995  logChecker("CheckBufferOverrun::analyseWholeProgram");
996 
997  const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> callsMap = ctu->getCallsMap();
998 
999  for (const Check::FileInfo* fi1 : fileInfo) {
1000  const MyFileInfo *fi = dynamic_cast<const MyFileInfo*>(fi1);
1001  if (!fi)
1002  continue;
1003  for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex)
1004  foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 1, errorLogger);
1005  for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith)
1006  foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 2, errorLogger);
1007  }
1008  return foundErrors;
1009 }
1010 
1011 bool CheckBufferOverrun::analyseWholeProgram1(const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger)
1012 {
1013  const CTU::FileInfo::FunctionCall *functionCall = nullptr;
1014 
1015  const std::list<ErrorMessage::FileLocation> &locationList =
1017  unsafeUsage,
1018  callsMap,
1019  "Using argument ARG",
1020  &functionCall,
1021  false);
1022  if (locationList.empty())
1023  return false;
1024 
1025  const char *errorId = nullptr;
1026  std::string errmsg;
1027  CWE cwe(0);
1028 
1029  if (type == 1) {
1030  errorId = "ctuArrayIndex";
1031  if (unsafeUsage.value > 0)
1032  errmsg = "Array index out of bounds; '" + unsafeUsage.myArgumentName + "' buffer size is " + std::to_string(functionCall->callArgValue) + " and it is accessed at offset " + std::to_string(unsafeUsage.value) + ".";
1033  else
1034  errmsg = "Array index out of bounds; buffer '" + unsafeUsage.myArgumentName + "' is accessed at offset " + std::to_string(unsafeUsage.value) + ".";
1035  cwe = (unsafeUsage.value > 0) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN;
1036  } else {
1037  errorId = "ctuPointerArith";
1038  errmsg = "Pointer arithmetic overflow; '" + unsafeUsage.myArgumentName + "' buffer size is " + std::to_string(functionCall->callArgValue);
1040  }
1041 
1042  const ErrorMessage errorMessage(locationList,
1043  emptyString,
1045  errmsg,
1046  errorId,
1047  cwe, Certainty::normal);
1048  errorLogger.reportErr(errorMessage);
1049 
1050  return true;
1051 }
1052 
1054 {
1055  logChecker("CheckBufferOverrun::objectIndex");
1056  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1057  for (const Scope *functionScope : symbolDatabase->functionScopes) {
1058  for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
1059  if (!Token::simpleMatch(tok, "["))
1060  continue;
1061  const Token *obj = tok->astOperand1();
1062  const Token *idx = tok->astOperand2();
1063  if (!idx || !obj)
1064  continue;
1065  if (idx->hasKnownIntValue()) {
1066  if (idx->getKnownIntValue() == 0)
1067  continue;
1068  }
1069  if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0)
1070  continue;
1071 
1072  std::vector<ValueFlow::Value> values = ValueFlow::getLifetimeObjValues(obj, false, -1);
1073  for (const ValueFlow::Value& v:values) {
1074  if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)
1075  continue;
1076  const Variable *var = v.tokvalue->variable();
1077  if (!var)
1078  continue;
1079  if (var->isReference())
1080  continue;
1081  if (var->isRValueReference())
1082  continue;
1083  if (var->isArray())
1084  continue;
1085  if (var->isPointer()) {
1086  if (!var->valueType())
1087  continue;
1088  if (!obj->valueType())
1089  continue;
1090  if (var->valueType()->pointer > obj->valueType()->pointer)
1091  continue;
1092  }
1093  if (obj->valueType() && var->valueType() && (obj->isCast() || (obj->isCpp() && isCPPCast(obj)) || obj->valueType()->pointer)) { // allow cast to a different type
1094  const auto varSize = var->valueType()->typeSize(mSettings->platform);
1095  if (varSize == 0)
1096  continue;
1097  if (obj->valueType()->type != var->valueType()->type) {
1098  if (ValueFlow::isOutOfBounds(makeSizeValue(varSize, v.path), idx).empty())
1099  continue;
1100  }
1101  }
1102  if (v.path != 0) {
1103  std::vector<ValueFlow::Value> idxValues;
1104  std::copy_if(idx->values().cbegin(),
1105  idx->values().cend(),
1106  std::back_inserter(idxValues),
1107  [&](const ValueFlow::Value& vidx) {
1108  if (!vidx.isIntValue())
1109  return false;
1110  return vidx.path == v.path || vidx.path == 0;
1111  });
1112  if (std::any_of(idxValues.cbegin(), idxValues.cend(), [&](const ValueFlow::Value& vidx) {
1113  if (vidx.isImpossible())
1114  return (vidx.intvalue == 0);
1115  return (vidx.intvalue != 0);
1116  })) {
1117  objectIndexError(tok, &v, idx->hasKnownIntValue());
1118  }
1119  } else {
1120  objectIndexError(tok, &v, idx->hasKnownIntValue());
1121  }
1122  }
1123  }
1124  }
1125 }
1126 
1127 void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known)
1128 {
1129  ErrorPath errorPath;
1130  std::string name;
1131  if (v) {
1132  const Token* expr = v->tokvalue;
1133  while (Token::simpleMatch(expr->astParent(), "."))
1134  expr = expr->astParent();
1135  name = expr->expressionString();
1136  errorPath = v->errorPath;
1137  }
1138  errorPath.emplace_back(tok, "");
1139  std::string verb = known ? "is" : "might be";
1140  reportError(errorPath,
1142  "objectIndex",
1143  "The address of variable '" + name + "' " + verb + " accessed at non-zero index.",
1144  CWE758,
1146 }
1147 
1148 static bool isVLAIndex(const Token* tok)
1149 {
1150  if (!tok)
1151  return false;
1152  if (tok->varId() != 0U)
1153  return true;
1154  if (tok->str() == "?") {
1155  // this is a VLA index if both expressions around the ":" is VLA index
1156  return tok->astOperand2() &&
1157  tok->astOperand2()->str() == ":" &&
1158  isVLAIndex(tok->astOperand2()->astOperand1()) &&
1159  isVLAIndex(tok->astOperand2()->astOperand2());
1160  }
1161  return isVLAIndex(tok->astOperand1()) || isVLAIndex(tok->astOperand2());
1162 }
1163 
1165 {
1166  logChecker("CheckBufferOverrun::negativeArraySize");
1167  const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
1168  for (const Variable* var : symbolDatabase->variableList()) {
1169  if (!var || !var->isArray())
1170  continue;
1171  const Token* const nameToken = var->nameToken();
1172  if (!Token::Match(nameToken, "%var% [") || !nameToken->next()->astOperand2())
1173  continue;
1174  const ValueFlow::Value* sz = nameToken->next()->astOperand2()->getValueLE(-1, *mSettings);
1175  // don't warn about constant negative index because that is a compiler error
1176  if (sz && isVLAIndex(nameToken->next()->astOperand2()))
1177  negativeArraySizeError(nameToken);
1178  }
1179 
1180  for (const Scope* functionScope : symbolDatabase->functionScopes) {
1181  for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
1182  if (!tok->isKeyword() || tok->str() != "new" || !tok->astOperand1() || tok->astOperand1()->str() != "[")
1183  continue;
1184  const Token* valOperand = tok->astOperand1()->astOperand2();
1185  if (!valOperand)
1186  continue;
1187  const ValueFlow::Value* sz = valOperand->getValueLE(-1, *mSettings);
1188  if (sz)
1190  }
1191  }
1192 }
1193 
1195 {
1196  const std::string arrayName = tok ? tok->expressionString() : std::string();
1197  const std::string line1 = arrayName.empty() ? std::string() : ("$symbol:" + arrayName + '\n');
1198  reportError(tok, Severity::error, "negativeArraySize",
1199  line1 +
1200  "Declaration of array '" + arrayName + "' with negative size is undefined behaviour", CWE758, Certainty::normal);
1201 }
1202 
1204 {
1205  const std::string msg = "Memory allocation size is negative.";
1206  const ErrorPath errorPath = getErrorPath(tok, value, msg);
1207  const bool inconclusive = value != nullptr && !value->isKnown();
1208  reportError(errorPath, inconclusive ? Severity::warning : Severity::error, "negativeMemoryAllocationSize",
1210 }
bool astIsContainer(const Token *tok)
Definition: astutils.cpp:244
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:3078
bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Settings &settings, bool pure, bool followVar, ErrorPath *errors)
Definition: astutils.cpp:1549
Token * findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings &settings, int depth)
Definition: astutils.cpp:2851
bool isCPPCast(const Token *tok)
Definition: astutils.cpp:3242
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
static bool isVLAIndex(const Token *tok)
static bool getDimensionsEtc(const Token *const arrayToken, const Settings &settings, std::vector< Dimension > &dimensions, ErrorPath &errorPath, bool &mightBeLarger, MathLib::bigint &path)
static const CWE CWE_BUFFER_UNDERRUN(786U)
static const CWE CWE_ARGUMENT_SIZE(398U)
static std::string stringifyIndexes(const std::string &array, const std::vector< ValueFlow::Value > &indexValues)
static const CWE CWE758(758U)
static const ValueFlow::Value * getBufferSizeValue(const Token *tok)
static const CWE CWE_BUFFER_OVERRUN(788U)
static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U)
static std::vector< ValueFlow::Value > getOverrunIndexValues(const Token *tok, const Token *arrayToken, const std::vector< Dimension > &dimensions, const std::vector< const Token * > &indexTokens, MathLib::bigint path)
static ValueFlow::Value makeSizeValue(MathLib::bigint size, MathLib::bigint path)
static const CWE CWE170(170U)
static const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U)
static const CWE CWE131(131U)
static int getMinFormatStringOutputLength(const std::vector< const Token * > &parameters, nonneg int formatStringArgNr)
static std::string arrayIndexMessage(const Token *tok, const std::vector< Dimension > &dimensions, const std::vector< ValueFlow::Value > &indexValues, const Token *condition)
static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector< const Token * > &args, const MathLib::bigint bufferSize, const Settings &settings, const Tokenizer *tokenizer)
MathLib::bigint callArgValue
Definition: ctu.h:99
static std::list< ErrorMessage::FileLocation > getErrorPath(InvalidValueType invalidValue, const UnsafeUsage &unsafeUsage, const std::map< std::string, std::list< const CallBase * >> &callsMap, const char info[], const FunctionCall **const functionCallPtr, bool warning)
Definition: ctu.cpp:554
std::map< std::string, std::list< const CallBase * > > getCallsMap() const
Definition: ctu.cpp:247
buffer overruns and array index out of bounds
void arrayIndexThenCheckError(const Token *tok, const std::string &indexName)
bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list< Check::FileInfo * > &fileInfo, const Settings &settings, ErrorLogger &errorLogger) override
Analyse all file infos for all TU.
static bool isCtuUnsafePointerArith(const Settings &settings, const Token *argtok, MathLib::bigint *offset)
static bool isCtuUnsafeBufferUsage(const Settings &settings, const Token *argtok, MathLib::bigint *offset, int type)
static bool isCtuUnsafeArrayIndex(const Settings &settings, const Token *argtok, MathLib::bigint *offset)
Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override
void negativeIndexError(const Token *tok, const std::vector< Dimension > &dimensions, const std::vector< ValueFlow::Value > &indexes)
ValueFlow::Value getBufferSize(const Token *bufTok) const
void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue)
void negativeMemoryAllocationSizeError(const Token *tok, const ValueFlow::Value *value)
void arrayIndexError(const Token *tok, const std::vector< Dimension > &dimensions, const std::vector< ValueFlow::Value > &indexes)
static bool analyseWholeProgram1(const std::map< std::string, std::list< const CTU::FileInfo::CallBase * >> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger)
void terminateStrncpyError(const Token *tok, const std::string &varname)
void negativeArraySizeError(const Token *tok)
void argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string &paramExpression, const Variable *paramVar, const Variable *functionArg)
Check::FileInfo * getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override
Parse current TU and extract file info.
void bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty)
void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known)
Base class used for whole-program analysis.
Definition: check.h:103
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
ErrorPath getErrorPath(const Token *errtok, const ValueFlow::Value *value, std::string bug) const
Definition: check.cpp:111
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
const std::string & name() const
class name, used to generate documentation
Definition: check.h:88
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
virtual void reportErr(const ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:48
const Variable * getArgumentVar(nonneg int num) const
bool hasminsize(const Token *ftok) const
Definition: library.cpp:1469
bool isargformatstr(const Token *ftok, int argnr) const
Definition: library.h:344
const std::vector< ArgumentChecks::MinSize > * argminsizes(const Token *ftok, int argnr) const
Definition: library.h:369
long long bigint
Definition: mathlib.h:68
std::size_t sizeof_pointer
Definition: platform.h:102
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
Library library
Library.
Definition: settings.h:237
Platform platform
Definition: settings.h:255
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:557
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
const std::vector< const Variable * > & variableList() const
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
bool hasKnownValue() const
Definition: token.cpp:2562
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
const std::string & originalName() const
Definition: token.h:1193
bool hasKnownIntValue() const
Definition: token.cpp:2553
MathLib::bigint getKnownIntValue() const
Definition: token.h:1218
const Token * getValueTokenMaxStrLength() const
Definition: token.cpp:2042
const Token * getValueTokenMinStrSize(const Settings &settings, MathLib::bigint *path=nullptr) const
Definition: token.cpp:2022
bool isCpp() const
Definition: token.cpp:2752
static nonneg int getStrLength(const Token *tok)
Definition: token.cpp:811
const ValueType * valueType() const
Definition: token.h:331
void astOperand1(Token *tok)
Definition: token.cpp:1490
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
bool isCast() const
Definition: token.h:458
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings &settings) const
Definition: token.cpp:1988
static nonneg int getStrArraySize(const Token *tok)
Definition: token.cpp:841
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1502
void scope(const Scope *s)
Associate this token with given scope.
Definition: token.h:1042
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:447
@ eString
Definition: token.h:162
@ eLogicalOp
Definition: token.h:163
const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings &settings) const
Definition: token.cpp:1979
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
Token * next()
Definition: token.h:830
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1197
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
The main purpose is to tokenize the source code.
Definition: tokenize.h:46
nonneg int sizeOfType(const Token *type) const
Calculates sizeof value for given type.
Definition: tokenize.cpp:191
const Token * tokens() const
Definition: tokenize.h:592
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
enum ValueFlow::Value::ValueType valueType
MathLib::bigint path
Path id.
Definition: vfvalue.h:307
ErrorPath errorPath
Definition: vfvalue.h:282
bool errorSeverity() const
Definition: vfvalue.h:387
bool isKnown() const
Definition: vfvalue.h:353
static Value unknown()
Definition: vfvalue.h:54
const Token * condition
Condition that this value depends on.
Definition: vfvalue.h:280
const Token * tokvalue
token value - the token that has the value.
Definition: vfvalue.h:271
long long intvalue
int value (or sometimes bool value?)
Definition: vfvalue.h:268
bool isBufferSizeValue() const
Definition: vfvalue.h:232
void setKnown()
Definition: vfvalue.h:349
bool isInconclusive() const
Definition: vfvalue.h:378
bool isFloat() const
enum ValueType::Type type
MathLib::bigint typeSize(const Platform &platform, bool p=false) const
bool isIntegral() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isReference() const
Is reference variable.
bool isRValueReference() const
Is reference variable.
const Scope * scope() const
Get Scope pointer of enclosing scope.
const std::string & name() const
Get name string.
bool isPointerArray() const
Is variable an array of pointers.
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).
const std::vector< Dimension > & dimensions() const
Get array dimensions.
bool isPointer() const
Is pointer variable.
const ValueType * valueType() const
std::string toString(Color c)
Definition: color.cpp:54
static const std::string emptyString
Definition: config.h:127
#define nonneg
Definition: config.h:138
Certainty
Definition: errortypes.h:54
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130
@ warning
Warning.
@ portability
Portability warning.
@ style
Style warning.
@ error
Programming error.
CPPCHECKLIB std::list< FileInfo::UnsafeUsage > getUnsafeUsage(const Tokenizer &tokenizer, const Settings &settings, bool(*isUnsafeUsage)(const Settings &settings, const Token *argtok, MathLib::bigint *value))
Definition: ctu.cpp:472
CPPCHECKLIB std::string toString(const std::list< FileInfo::UnsafeUsage > &unsafeUsage)
Definition: ctu.cpp:151
CPPCHECKLIB std::list< FileInfo::UnsafeUsage > loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement)
Definition: ctu.cpp:257
std::string eitherTheConditionIsRedundant(const Token *condition)
Definition: valueflow.cpp:9680
std::vector< Value > isOutOfBounds(const Value &size, const Token *indexTok, bool possible=true)
Definition: valueflow.cpp:9757
CPPCHECKLIB std::vector< Value > getLifetimeObjValues(const Token *tok, bool inconclusive=false, MathLib::bigint path=0)
Definition: valueflow.cpp:3528
MathLib::bigint value
Definition: ctu.h:74
std::string myArgumentName
Definition: ctu.h:72
Array dimension information.
const Token * tok
size token
MathLib::bigint num
(assumed) dimension length when size is a number, 0 if not known
bool known
Known size.
static const char * getOrdinalText(int i)
Definition: utils.h:189