Cppcheck
checkfunctions.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 // Check functions
21 //---------------------------------------------------------------------------
22 
23 #include "checkfunctions.h"
24 
25 #include "astutils.h"
26 #include "mathlib.h"
27 #include "platform.h"
28 #include "standards.h"
29 #include "symboldatabase.h"
30 #include "token.h"
31 #include "tokenize.h"
32 #include "valueflow.h"
33 #include "vfvalue.h"
34 
35 #include <iomanip>
36 #include <list>
37 #include <sstream>
38 #include <unordered_map>
39 #include <vector>
40 
41 //---------------------------------------------------------------------------
42 
43 
44 // Register this check class (by creating a static instance of it)
45 namespace {
46  CheckFunctions instance;
47 }
48 
49 static const CWE CWE252(252U); // Unchecked Return Value
50 static const CWE CWE477(477U); // Use of Obsolete Functions
51 static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
52 static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments
53 static const CWE CWE686(686U); // Function Call With Incorrect Argument Type
54 static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value
55 static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument
56 
58 {
60 
61  logChecker("CheckFunctions::checkProhibitedFunctions");
62 
63  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
64  for (const Scope *scope : symbolDatabase->functionScopes) {
65  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
66  if (!Token::Match(tok, "%name% (") && tok->varId() == 0)
67  continue;
68  // alloca() is special as it depends on the code being C or C++, so it is not in Library
69  if (checkAlloca && Token::simpleMatch(tok, "alloca (") && (!tok->function() || tok->function()->nestedIn->type == Scope::eGlobal)) {
70  if (tok->isC()) {
72  reportError(tok, Severity::warning, "allocaCalled",
73  "$symbol:alloca\n"
74  "Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n"
75  "The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or "
76  "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
77  "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca).");
78  } else
79  reportError(tok, Severity::warning, "allocaCalled",
80  "$symbol:alloca\n"
81  "Obsolete function 'alloca' called.\n"
82  "The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or "
83  "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
84  "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca).");
85  } else {
86  if (tok->function() && tok->function()->hasBody())
87  continue;
88 
90  if (wi) {
91  if (mSettings->severity.isEnabled(wi->severity) && ((tok->isC() && mSettings->standards.c >= wi->standards.c) || (tok->isCpp() && mSettings->standards.cpp >= wi->standards.cpp))) {
92  const std::string daca = mSettings->daca ? "prohibited" : "";
93  reportError(tok, wi->severity, daca + tok->str() + "Called", wi->message, CWE477, Certainty::normal);
94  }
95  }
96  }
97  }
98  }
99 }
100 
101 //---------------------------------------------------------------------------
102 // Check <valid>, <strz> and <not-bool>
103 //---------------------------------------------------------------------------
105 {
106  logChecker("CheckFunctions::invalidFunctionUsage");
107  const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
108  for (const Scope *scope : symbolDatabase->functionScopes) {
109  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
110  if (!Token::Match(tok, "%name% ( !!)"))
111  continue;
112  const Token * const functionToken = tok;
113  const std::vector<const Token *> arguments = getArguments(tok);
114  for (int argnr = 1; argnr <= arguments.size(); ++argnr) {
115  const Token * const argtok = arguments[argnr-1];
116 
117  // check <valid>...</valid>
118  const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,*mSettings);
119  if (invalidValue) {
120  invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, mSettings->library.validarg(functionToken, argnr));
121  }
122 
123  if (astIsBool(argtok)) {
124  // check <not-bool>
125  if (mSettings->library.isboolargbad(functionToken, argnr))
126  invalidFunctionArgBoolError(argtok, functionToken->str(), argnr);
127 
128  // Are the values 0 and 1 valid?
129  else if (!mSettings->library.isIntArgValid(functionToken, argnr, 0))
130  invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
131  else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1))
132  invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
133  }
134  // check <strz>
135  if (mSettings->library.isargstrz(functionToken, argnr)) {
136  if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) {
137  const ValueType * valueType = argtok->next()->valueType();
138  const Variable * variable = argtok->next()->variable();
139  if ((valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T || (valueType->type == ValueType::Type::RECORD && Token::Match(argtok, "& %var% . %var% ,|)"))) &&
140  !variable->isArray() &&
141  (variable->isConst() || !variable->isGlobal()) &&
142  (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) {
143  invalidFunctionArgStrError(argtok, functionToken->str(), argnr);
144  }
145  }
146  const ValueType* const valueType = argtok->valueType();
147  const Variable* const variable = argtok->variable();
148  // Is non-null terminated local variable of type char (e.g. char buf[] = {'x'};) ?
149  if (variable && variable->isLocal()
150  && valueType && (valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T)
151  && !isVariablesChanged(variable->declEndToken(), functionToken, 0 /*indirect*/, { variable }, *mSettings)) {
152  const Token* varTok = variable->declEndToken();
153  auto count = -1; // Find out explicitly set count, e.g.: char buf[3] = {...}. Variable 'count' is set to 3 then.
154  if (varTok && Token::simpleMatch(varTok->astOperand1(), "["))
155  {
156  const Token* const countTok = varTok->astOperand1()->astOperand2();
157  if (countTok && countTok->hasKnownIntValue())
158  count = countTok->getKnownIntValue();
159  }
160  if (Token::simpleMatch(varTok, "= {")) {
161  varTok = varTok->tokAt(1);
162  auto charsUntilFirstZero = 0;
163  bool search = true;
164  while (search && varTok && !Token::simpleMatch(varTok->next(), "}")) {
165  varTok = varTok->next();
166  if (!Token::simpleMatch(varTok, ",")) {
167  if (Token::Match(varTok, "%op%")) {
168  varTok = varTok->next();
169  continue;
170  }
171  ++charsUntilFirstZero;
172  if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() == 0)
173  search=false; // stop counting for cases like char buf[3] = {'x', '\0', 'y'};
174  }
175  }
176  if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() != 0
177  && (count == -1 || (count > 0 && count <= charsUntilFirstZero))) {
178  invalidFunctionArgStrError(argtok, functionToken->str(), argnr);
179  }
180  } else if (count > -1 && Token::Match(varTok, "= %str%")) {
181  const Token* strTok = varTok->getValueTokenMinStrSize(*mSettings);
182  if (strTok) {
183  const int strSize = Token::getStrArraySize(strTok);
184  if (strSize > count && strTok->str().find('\0') == std::string::npos)
185  invalidFunctionArgStrError(argtok, functionToken->str(), argnr);
186  }
187  }
188  }
189  }
190  }
191  }
192  }
193 }
194 
195 void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr)
196 {
197  std::ostringstream errmsg;
198  errmsg << "$symbol:" << functionName << '\n';
199  if (invalidValue && invalidValue->condition)
200  errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition)
201  << " or $symbol() argument nr " << argnr << " can have invalid value.";
202  else
203  errmsg << "Invalid $symbol() argument nr " << argnr << '.';
204  if (invalidValue)
205  errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'.";
206  else
207  errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'.";
208  if (invalidValue)
209  reportError(getErrorPath(tok, invalidValue, "Invalid argument"),
210  invalidValue->errorSeverity() && invalidValue->isKnown() ? Severity::error : Severity::warning,
211  "invalidFunctionArg",
212  errmsg.str(),
213  CWE628,
215  else
216  reportError(tok,
218  "invalidFunctionArg",
219  errmsg.str(),
220  CWE628,
222 }
223 
224 void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
225 {
226  std::ostringstream errmsg;
227  errmsg << "$symbol:" << functionName << '\n';
228  errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required.";
229  reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, Certainty::normal);
230 }
231 
232 void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr)
233 {
234  std::ostringstream errmsg;
235  errmsg << "$symbol:" << functionName << '\n';
236  errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required.";
237  reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, Certainty::normal);
238 }
239 
240 //---------------------------------------------------------------------------
241 // Check for ignored return values.
242 //---------------------------------------------------------------------------
244 {
247  !mSettings->isPremiumEnabled("ignoredReturnValue"))
248  return;
249 
250  logChecker("CheckFunctions::checkIgnoredReturnValue"); // style,warning
251 
252  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
253  for (const Scope *scope : symbolDatabase->functionScopes) {
254  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
255  // skip c++11 initialization, ({...})
256  if (Token::Match(tok, "%var%|(|,|return {"))
257  tok = tok->linkAt(1);
258  else if (Token::Match(tok, "[(<]") && tok->link())
259  tok = tok->link();
260 
261  if (tok->varId() || tok->isKeyword() || tok->isStandardType() || !Token::Match(tok, "%name% ("))
262  continue;
263 
264  const Token *parent = tok->next()->astParent();
265  while (Token::Match(parent, "%cop%")) {
266  if (Token::Match(parent, "<<|>>|*") && !parent->astParent())
267  break;
268  parent = parent->astParent();
269  }
270  if (parent)
271  continue;
272 
273  if (!tok->scope()->isExecutable()) {
274  tok = tok->scope()->bodyEnd;
275  continue;
276  }
277 
278  if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) &&
279  tok->next()->astOperand1()) {
281  const bool warn = (tok->function() && tok->function()->isAttributeNodiscard()) || // avoid duplicate warnings for resource-allocating functions
282  (retvalTy == Library::UseRetValType::DEFAULT && mSettings->library.getAllocFuncInfo(tok) == nullptr);
284  ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString());
287  ignoredReturnErrorCode(tok, tok->next()->astOperand1()->expressionString());
288  }
289  }
290  }
291 }
292 
293 void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function)
294 {
295  reportError(tok, Severity::warning, "ignoredReturnValue",
296  "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, Certainty::normal);
297 }
298 
299 void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& function)
300 {
301  reportError(tok, Severity::style, "ignoredReturnErrorCode",
302  "$symbol:" + function + "\nError code from the return value of function $symbol() is not used.", CWE252, Certainty::normal);
303 }
304 
305 //---------------------------------------------------------------------------
306 // Check for ignored return values.
307 //---------------------------------------------------------------------------
308 static const Token *checkMissingReturnScope(const Token *tok, const Library &library);
309 
311 {
312  logChecker("CheckFunctions::checkMissingReturn");
313  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
314  for (const Scope *scope : symbolDatabase->functionScopes) {
315  const Function *function = scope->function;
316  if (!function || !function->hasBody())
317  continue;
318  if (function->name() == "main" && !(mTokenizer->isC() && mSettings->standards.c < Standards::C99))
319  continue;
320  if (function->type != Function::Type::eFunction && function->type != Function::Type::eOperatorEqual)
321  continue;
322  if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName())
323  continue;
324  if (Function::returnsVoid(function, true))
325  continue;
326  const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library);
327  if (errorToken)
328  missingReturnError(errorToken);
329  }
330 }
331 
332 static bool isForwardJump(const Token *gotoToken)
333 {
334  if (!Token::Match(gotoToken, "goto %name% ;"))
335  return false;
336  for (const Token *prev = gotoToken; gotoToken; gotoToken = gotoToken->previous()) {
337  if (Token::Match(prev, "%name% :") && prev->str() == gotoToken->next()->str())
338  return true;
339  if (prev->str() == "{" && prev->scope()->type == Scope::eFunction)
340  return false;
341  }
342  return false;
343 }
344 
345 static const Token *checkMissingReturnScope(const Token *tok, const Library &library)
346 {
347  const Token *lastStatement = nullptr;
348  while ((tok = tok->previous()) != nullptr) {
349  if (tok->str() == ")")
350  tok = tok->link();
351  if (tok->str() == "{")
352  return lastStatement ? lastStatement : tok->next();
353  if (tok->str() == "}") {
354  for (const Token *prev = tok->link()->previous(); prev && prev->scope() == tok->scope() && !Token::Match(prev, "[;{}]"); prev = prev->previous()) {
355  if (prev->isKeyword() && Token::Match(prev, "return|throw"))
356  return nullptr;
357  if (prev->str() == "goto" && !isForwardJump(prev))
358  return nullptr;
359  }
360  if (tok->scope()->type == Scope::ScopeType::eSwitch) {
361  // find reachable break / !default
362  bool hasDefault = false;
363  bool reachable = false;
364  for (const Token *switchToken = tok->link()->next(); switchToken != tok; switchToken = switchToken->next()) {
365  if (reachable && Token::simpleMatch(switchToken, "break ;")) {
366  if (Token::simpleMatch(switchToken->previous(), "}") && !checkMissingReturnScope(switchToken->previous(), library))
367  reachable = false;
368  else
369  return switchToken;
370  }
371  if (switchToken->isKeyword() && Token::Match(switchToken, "return|throw"))
372  reachable = false;
373  if (Token::Match(switchToken, "%name% (") && library.isnoreturn(switchToken))
374  reachable = false;
375  if (Token::Match(switchToken, "case|default"))
376  reachable = true;
377  if (Token::simpleMatch(switchToken, "default :"))
378  hasDefault = true;
379  else if (switchToken->str() == "{" && (switchToken->scope()->isLoopScope() || switchToken->scope()->type == Scope::ScopeType::eSwitch))
380  switchToken = switchToken->link();
381  }
382  if (!hasDefault)
383  return tok->link();
384  } else if (tok->scope()->type == Scope::ScopeType::eIf) {
385  const Token *condition = tok->scope()->classDef->next()->astOperand2();
386  if (condition && condition->hasKnownIntValue() && condition->getKnownIntValue() == 1)
387  return checkMissingReturnScope(tok, library);
388  return tok;
389  } else if (tok->scope()->type == Scope::ScopeType::eElse) {
390  const Token *errorToken = checkMissingReturnScope(tok, library);
391  if (errorToken)
392  return errorToken;
393  tok = tok->link();
394  if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
395  return checkMissingReturnScope(tok->tokAt(-2), library);
396  return tok;
397  }
398  // FIXME
399  return nullptr;
400  }
401  if (tok->isKeyword() && Token::Match(tok, "return|throw"))
402  return nullptr;
403  if (tok->str() == "goto" && !isForwardJump(tok))
404  return nullptr;
405  if (Token::Match(tok, "%name% (") && !library.isnotnoreturn(tok)) {
406  return nullptr;
407  }
408  if (Token::Match(tok, "[;{}] %name% :"))
409  return tok;
410  if (Token::Match(tok, "; !!}") && !lastStatement)
411  lastStatement = tok->next();
412  }
413  return nullptr;
414 }
415 
417 {
418  reportError(tok, Severity::error, "missingReturn",
419  "Found an exit path from function with non-void return type that has missing return statement", CWE758, Certainty::normal);
420 }
421 //---------------------------------------------------------------------------
422 // Detect passing wrong values to <cmath> functions like atan(0, x);
423 //---------------------------------------------------------------------------
425 {
427  const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
428 
429  if (!styleC99 && !printWarnings && !mSettings->isPremiumEnabled("wrongmathcall"))
430  return;
431 
432  logChecker("CheckFunctions::checkMathFunctions"); // style,warning,c99,c++11
433 
434  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
435  for (const Scope *scope : symbolDatabase->functionScopes) {
436  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
437  if (tok->varId())
438  continue;
439  if (printWarnings && Token::Match(tok, "%name% ( !!)")) {
440  if (tok->strAt(-1) != "."
441  && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) {
442  const std::string& number = tok->strAt(2);
443  if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= 0) ||
444  (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.))
446  } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) {
447  const std::string& number = tok->strAt(2);
448  if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= -1) ||
449  (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.))
451  }
452  // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined
453  else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) {
454  if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4)))
455  mathfunctionCallWarning(tok, 2);
456  }
457  // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined).
458  else if (Token::Match(tok, "fmod|fmodf|fmodl (")) {
459  const Token* nextArg = tok->tokAt(2)->nextArgument();
460  if (nextArg && MathLib::isNullValue(nextArg->str()))
461  mathfunctionCallWarning(tok, 2);
462  }
463  // pow ( x , y) If x is zero, and y is negative --> division by zero
464  else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) {
465  if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4)))
466  mathfunctionCallWarning(tok, 2);
467  }
468  }
469 
470  if (styleC99) {
471  if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) {
472  mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)");
473  } else if (Token::simpleMatch(tok, "exp (") && Token::Match(tok->linkAt(1), ") - %num%") && Tokenizer::isOneNumber(tok->linkAt(1)->strAt(2)) && tok->linkAt(1)->next()->astOperand1() == tok->next()) {
474  mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)");
475  } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) {
476  const Token* plus = tok->next()->astOperand2();
477  if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str()))))
478  mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)");
479  }
480  }
481  }
482  }
483 }
484 
485 void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam)
486 {
487  if (tok) {
488  if (numParam == 1)
489  reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal);
490  else if (numParam == 2)
491  reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal);
492  } else
493  reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, Certainty::normal);
494 }
495 
496 void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp)
497 {
498  reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, Certainty::normal);
499 }
500 
501 //---------------------------------------------------------------------------
502 // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted
503 //---------------------------------------------------------------------------
505 {
506 // FIXME:
507 // Replace this with library configuration.
508 // For instance:
509 // <arg nr="3">
510 // <warn knownIntValue="0" severity="warning" msg="..."/>
511 // </arg>
512 
514  return;
515 
516  logChecker("CheckFunctions::memsetZeroBytes"); // warning
517 
518  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
519  for (const Scope *scope : symbolDatabase->functionScopes) {
520  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
521  if (Token::Match(tok, "memset|wmemset (") && (numberOfArguments(tok)==3)) {
522  const std::vector<const Token *> &arguments = getArguments(tok);
523  if (WRONG_DATA(arguments.size() != 3U, tok))
524  continue;
525  const Token* lastParamTok = arguments[2];
526  if (MathLib::isNullValue(lastParamTok->str()))
528  }
529  }
530  }
531 }
532 
534 {
535  const std::string summary("memset() called to fill 0 bytes.");
536  const std::string verbose(summary + " The second and third arguments might be inverted."
537  " The function memset ( void * ptr, int value, size_t num ) sets the"
538  " first num bytes of the block of memory pointed by ptr to the specified value.");
539  reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, Certainty::normal);
540 }
541 
543 {
544 // FIXME:
545 // Replace this with library configuration.
546 // For instance:
547 // <arg nr="2">
548 // <not-float/>
549 // <warn possibleIntValue=":-129,256:" severity="warning" msg="..."/>
550 // </arg>
551 
552  const bool printPortability = mSettings->severity.isEnabled(Severity::portability);
553  const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
554  if (!printWarning && !printPortability)
555  return;
556 
557  logChecker("CheckFunctions::memsetInvalid2ndParam"); // warning,portability
558 
559  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
560  for (const Scope *scope : symbolDatabase->functionScopes) {
561  for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) {
562  if (!Token::simpleMatch(tok, "memset ("))
563  continue;
564 
565  const std::vector<const Token *> args = getArguments(tok);
566  if (args.size() != 3)
567  continue;
568 
569  // Second parameter is zero literal, i.e. 0.0f
570  const Token * const secondParamTok = args[1];
571  if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str()))
572  continue;
573 
574  // Check if second parameter is a float variable or a float literal != 0.0f
575  if (printPortability && astIsFloat(secondParamTok,false)) {
576  memsetFloatError(secondParamTok, secondParamTok->expressionString());
577  }
578 
579  if (printWarning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range
580  const long long int value = MathLib::toBigNumber(secondParamTok->str());
581  const long long sCharMin = mSettings->platform.signedCharMin();
582  const long long uCharMax = mSettings->platform.unsignedCharMax();
583  if (value < sCharMin || value > uCharMax)
584  memsetValueOutOfRangeError(secondParamTok, secondParamTok->str());
585  }
586  }
587  }
588 }
589 
590 void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value)
591 {
592  const std::string message("The 2nd memset() argument '" + var_value +
593  "' is a float, its representation is implementation defined.");
594  const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and"
595  " the actual representation of a floating-point value is implementation defined.");
596  reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, Certainty::normal);
597 }
598 
599 void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value)
600 {
601  const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'.");
602  const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value.");
603  reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose, CWE686, Certainty::normal);
604 }
605 
606 //---------------------------------------------------------------------------
607 // --check-library => warn for unconfigured functions
608 //---------------------------------------------------------------------------
609 
611 {
612  if (!mSettings->checkLibrary)
613  return;
614 
615  bool insideNew = false;
616  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
617  if (!tok->scope() || !tok->scope()->isExecutable())
618  continue;
619 
620  if (tok->str() == "new")
621  insideNew = true;
622  else if (tok->str() == ";")
623  insideNew = false;
624  else if (insideNew)
625  continue;
626 
627  if (tok->isKeyword() || !Token::Match(tok, "%name% ("))
628  continue;
629 
630  if (tok->varId() != 0 || tok->type() || tok->isStandardType())
631  continue;
632 
633  if (tok->linkAt(1)->strAt(1) == "(")
634  continue;
635 
636  if (tok->function())
637  continue;
638 
639  if (Token::simpleMatch(tok->astTop(), "throw"))
640  continue;
641 
642  if (Token::simpleMatch(tok->astParent(), ".")) {
643  const Token* contTok = tok->astParent()->astOperand1();
645  continue;
647  continue;
648  }
649 
651  continue;
652 
653  const std::string &functionName = mSettings->library.getFunctionName(tok);
654  if (functionName.empty())
655  continue;
656 
657  if (mSettings->library.functions.find(functionName) != mSettings->library.functions.end())
658  continue;
659 
660  if (mSettings->library.podtype(tok->expressionString()))
661  continue;
662 
663  if (mSettings->library.getTypeCheck("unusedvar", functionName) != Library::TypeCheck::def)
664  continue;
665 
666  const Token* start = tok;
667  while (Token::Match(start->tokAt(-2), "%name% ::") && !start->tokAt(-2)->isKeyword())
668  start = start->tokAt(-2);
670  continue;
671 
672  reportError(tok,
674  "checkLibraryFunction",
675  "--check-library: There is no matching configuration for function " + functionName + "()");
676  }
677 }
678 
679 // Check for problems to compiler apply (Named) Return Value Optimization for local variable
680 // Technically we have different guarantees between standard versions
681 // details: https://en.cppreference.com/w/cpp/language/copy_elision
683 {
685  return;
686 
688  return;
689 
690  logChecker("CheckFunctions::returnLocalStdMove"); // performance,c++11
691 
692  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
693  for (const Scope *scope : symbolDatabase->functionScopes) {
694  // Expect return by-value
695  if (Function::returnsReference(scope->function, /*unknown*/ true, /*includeRValueRef*/ true))
696  continue;
697  const auto rets = Function::findReturns(scope->function);
698  for (const Token* ret : rets) {
699  if (!Token::simpleMatch(ret->tokAt(-3), "std :: move ("))
700  continue;
701  const Token* retval = ret->astOperand2();
702  // NRVO
703  if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile())
704  copyElisionError(retval);
705  // RVO
706  if (Token::Match(retval, "(|{") && !retval->isCast() && !(retval->valueType() && retval->valueType()->reference != Reference::None))
707  copyElisionError(retval);
708  }
709  }
710 }
711 
713 {
714  reportError(tok,
716  "returnStdMoveLocal",
717  "Using std::move for returning object by-value from function will affect copy elision optimization."
718  " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local");
719 }
720 
722 {
724  return;
725 
726  logChecker("CheckFunctions::useStandardLibrary"); // style
727 
728  for (const Scope& scope: mTokenizer->getSymbolDatabase()->scopeList) {
729  if (scope.type != Scope::ScopeType::eFor)
730  continue;
731 
732  const Token *forToken = scope.classDef;
733  // for ( initToken ; condToken ; stepToken )
734  const Token* initToken = getInitTok(forToken);
735  if (!initToken)
736  continue;
737  const Token* condToken = getCondTok(forToken);
738  if (!condToken)
739  continue;
740  const Token* stepToken = getStepTok(forToken);
741  if (!stepToken)
742  continue;
743 
744  // 1. we expect that idx variable will be initialized with 0
745  const Token* idxToken = initToken->astOperand1();
746  const Token* initVal = initToken->astOperand2();
747  if (!idxToken || !initVal || !initVal->hasKnownIntValue() || initVal->getKnownIntValue() != 0)
748  continue;
749  const auto idxVarId = idxToken->varId();
750  if (0 == idxVarId)
751  continue;
752 
753  // 2. we expect that idx will be less of some variable
754  if (!condToken->isComparisonOp())
755  continue;
756 
757  const auto& secondOp = condToken->str();
758  const bool isLess = "<" == secondOp &&
760  condToken->astOperand1()->varId() == idxVarId;
761  const bool isMore = ">" == secondOp &&
763  condToken->astOperand2()->varId() == idxVarId;
764 
765  if (!(isLess || isMore))
766  continue;
767 
768  // 3. we expect idx incrementing by 1
769  const bool inc = stepToken->str() == "++" && stepToken->astOperand1() && stepToken->astOperand1()->varId() == idxVarId;
770  const bool plusOne = stepToken->isBinaryOp() && stepToken->str() == "+=" &&
771  stepToken->astOperand1()->varId() == idxVarId &&
772  stepToken->astOperand2()->str() == "1";
773  if (!inc && !plusOne)
774  continue;
775 
776  // technically using void* here is not correct but some compilers could allow it
777 
778  const Token *tok = scope.bodyStart;
779  const std::string memcpyName = tok->isCpp() ? "std::memcpy" : "memcpy";
780  // (reinterpret_cast<uint8_t*>(dest))[i] = (reinterpret_cast<const uint8_t*>(src))[i];
781  if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = "
782  "(| reinterpret_cast < const| uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] ; }", idxVarId)) {
783  useStandardLibraryError(tok->next(), memcpyName);
784  continue;
785  }
786 
787  // ((char*)dst)[i] = ((const char*)src)[i];
788  if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = "
789  "( ( const| uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] ; }", idxVarId)) {
790  useStandardLibraryError(tok->next(), memcpyName);
791  continue;
792  }
793 
794 
795  const static std::string memsetName = tok->isCpp() ? "std::memset" : "memset";
796  // ((char*)dst)[i] = 0;
797  if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = %char%|%num% ; }", idxVarId)) {
798  useStandardLibraryError(tok->next(), memsetName);
799  continue;
800  }
801 
802  // ((char*)dst)[i] = (const char*)0;
803  if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = "
804  "( const| uint8_t|int8_t|char ) (| %char%|%num% )| ; }", idxVarId)) {
805  useStandardLibraryError(tok->next(), memsetName);
806  continue;
807  }
808 
809  // (reinterpret_cast<uint8_t*>(dest))[i] = static_cast<const uint8_t>(0);
810  if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = "
811  "(| static_cast < const| uint8_t|int8_t|char > ( %char%|%num% ) )| ; }", idxVarId)) {
812  useStandardLibraryError(tok->next(), memsetName);
813  continue;
814  }
815 
816  // (reinterpret_cast<int8_t*>(dest))[i] = 0;
817  if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = "
818  "%char%|%num% ; }", idxVarId)) {
819  useStandardLibraryError(tok->next(), memsetName);
820  continue;
821  }
822  }
823 }
824 
825 void CheckFunctions::useStandardLibraryError(const Token *tok, const std::string& expected)
826 {
828  "useStandardLibrary",
829  "Consider using " + expected + " instead of loop.");
830 }
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:3078
bool astIsBool(const Token *tok)
Is expression of boolean type?
Definition: astutils.cpp:215
Token * getInitTok(Token *tok)
Definition: astutils.cpp:891
Library::Container::Action astContainerAction(const Token *tok, const Token **ftok)
Definition: astutils.cpp:287
Token * getCondTok(Token *tok)
Definition: astutils.cpp:873
bool astIsFloat(const Token *tok, bool unknown)
Is expression of floating point type?
Definition: astutils.cpp:207
Token * getStepTok(Token *tok)
Definition: astutils.cpp:898
bool isVariablesChanged(const Token *start, const Token *end, int indirect, const std::vector< const Variable * > &vars, const Settings &settings)
Definition: astutils.cpp:2891
bool isConstExpression(const Token *tok, const Library &library)
Definition: astutils.cpp:2044
Library::Container::Yield astContainerYield(const Token *tok, const Token **ftok)
Definition: astutils.cpp:297
int numberOfArguments(const Token *ftok)
Determines the number of arguments - if token is a function call or macro.
Definition: astutils.cpp:3058
#define WRONG_DATA(COND, TOK)
Use WRONG_DATA in checkers to mark conditions that check that data is correct.
Definition: check.h:50
static const CWE CWE252(252U)
static const CWE CWE758(758U)
static const CWE CWE687(687U)
static const CWE CWE477(477U)
static const CWE CWE686(686U)
static bool isForwardJump(const Token *gotoToken)
static const CWE CWE688(688U)
static const CWE CWE628(628U)
static const Token * checkMissingReturnScope(const Token *tok, const Library &library)
Check for bad function usage.
void checkMissingReturn()
Check for missing "return"
void checkProhibitedFunctions()
Check for functions that should not be used.
void memsetZeroBytes()
Check for filling zero bytes with memset()
void ignoredReturnErrorCode(const Token *tok, const std::string &function)
void ignoredReturnValueError(const Token *tok, const std::string &function)
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr)
void memsetZeroBytesError(const Token *tok)
void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr)
void checkIgnoredReturnValue()
Check for ignored return values.
void missingReturnError(const Token *tok)
void memsetValueOutOfRangeError(const Token *tok, const std::string &value)
void invalidFunctionUsage()
Invalid function usage (invalid input value / overlapping data)
void checkLibraryMatchFunctions()
–check-library: warn for unconfigured function calls
void mathfunctionCallWarning(const Token *tok, const nonneg int numParam=1)
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
void useStandardLibraryError(const Token *tok, const std::string &expected)
void memsetInvalid2ndParam()
Check for invalid 2nd parameter of memset()
void checkMathFunctions()
Check for parameters given to math function that do not make sense
void copyElisionError(const Token *tok)
void returnLocalStdMove()
Check for copy elision by RVO|NRVO
void memsetFloatError(const Token *tok, const std::string &var_value)
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
ErrorPath getErrorPath(const Token *errtok, const ValueFlow::Value *value, std::string bug) const
Definition: check.cpp:111
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
static std::vector< const Token * > findReturns(const Function *f)
static bool returnsReference(const Function *function, bool unknown=false, bool includeRValueRef=false)
static bool returnsVoid(const Function *function, bool unknown=false)
Library definitions handling.
Definition: library.h:52
const Container * detectContainerOrIterator(const Token *typeStart, bool *isIterator=nullptr, bool withoutStd=false) const
Definition: library.cpp:1241
const std::string & validarg(const Token *ftok, int argnr) const
Definition: library.h:357
bool isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const
Definition: library.cpp:925
bool isnoreturn(const Token *ftok) const
Definition: library.cpp:1542
bool isboolargbad(const Token *ftok, int argnr) const
Definition: library.h:336
bool isnotnoreturn(const Token *ftok) const
Definition: library.cpp:1563
UseRetValType getUseRetValType(const Token *ftok) const
Definition: library.cpp:1400
UseRetValType
Definition: library.h:182
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
const WarnInfo * getWarnInfo(const Token *ftok) const
Definition: library.cpp:1307
bool isargstrz(const Token *ftok, int argnr) const
Definition: library.h:349
TypeCheck getTypeCheck(std::string check, std::string typeName) const
Definition: library.cpp:1735
const PodType * podtype(const std::string &name) const
Definition: library.h:447
std::string getFunctionName(const Token *ftok) const
Get function name for function call.
Definition: library.cpp:1016
bool isNotLibraryFunction(const Token *ftok) const
Definition: library.cpp:1270
std::unordered_map< std::string, Function > functions
Definition: library.h:330
static bigint toBigNumber(const std::string &str)
for conversion of numeric literals - for atoi-like conversions please use strToInt()
Definition: mathlib.cpp:368
static bool isFloat(const std::string &str)
Definition: mathlib.cpp:535
static bool isNullValue(const std::string &str)
Does the string represent the numerical value of 0? In case leading or trailing white space is provid...
Definition: mathlib.cpp:1253
static bool isNegative(const std::string &str)
Definition: mathlib.cpp:636
static bool isInt(const std::string &str)
Definition: mathlib.cpp:1007
static double toDoubleNumber(const std::string &str)
for conversion of numeric literals
Definition: mathlib.cpp:487
long long unsignedCharMax() const
Definition: platform.h:175
long long signedCharMin() const
Definition: platform.h:183
Function * function
function info for this function
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
bool checkLibrary
Check for incomplete info in library files?
Definition: settings.h:135
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
bool daca
Are we running from DACA script?
Definition: settings.h:171
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
Standards standards
Struct contains standards settings.
Definition: settings.h:366
bool isEnabled(T flag) const
Definition: settings.h:66
std::vector< const Scope * > functionScopes
Fast access to function scopes.
std::list< Scope > scopeList
Information about all namespaces/classes/structures.
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
const ValueFlow::Value * getValue(const MathLib::bigint val) const
Definition: token.cpp:2596
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
bool isKeyword() const
Definition: token.h:358
bool hasKnownIntValue() const
Definition: token.cpp:2553
MathLib::bigint getKnownIntValue() const
Definition: token.h:1218
const Token * getValueTokenMinStrSize(const Settings &settings, MathLib::bigint *path=nullptr) const
Definition: token.cpp:2022
bool isCpp() const
Definition: token.cpp:2752
const ValueType * valueType() const
Definition: token.h:331
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
bool isNumber() const
Definition: token.h:371
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
bool isCast() const
Definition: token.h:458
static nonneg int getStrArraySize(const Token *tok)
Definition: token.cpp:841
const Token * tokAt(int index) const
Definition: token.cpp:427
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
Token * previous()
Definition: token.h:862
bool isBinaryOp() const
Definition: token.h:410
const ValueFlow::Value * getInvalidValue(const Token *ftok, nonneg int argnr, const Settings &settings) const
Definition: token.cpp:1997
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
bool isComparisonOp() const
Definition: token.h:398
Token * next()
Definition: token.h:830
const Token * nextArgument() const
Definition: token.cpp:903
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
const Token * tokens() const
Definition: tokenize.h:592
static bool isOneNumber(const std::string &s)
Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not?
Definition: tokenize.cpp:8134
bool isC() const
Is the code C.
Definition: tokenize.h:64
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool isCPP() const
Is the code CPP.
Definition: tokenize.h:69
bool isIntValue() const
Definition: vfvalue.h:211
bool errorSeverity() const
Definition: vfvalue.h:387
double floatValue
float value
Definition: vfvalue.h:274
bool isKnown() const
Definition: vfvalue.h:353
const Token * condition
Condition that this value depends on.
Definition: vfvalue.h:280
long long intvalue
int value (or sometimes bool value?)
Definition: vfvalue.h:268
bool isInconclusive() const
Definition: vfvalue.h:378
Value type.
enum ValueType::Type type
Reference reference
Is the outermost indirection of this type a reference or rvalue.
Information about a member variable.
bool isLocal() const
Is variable local.
const Token * declEndToken() const
Get end token of variable declaration E.g.
bool isGlobal() const
Is variable global.
bool isConst() const
Is variable const.
bool isArray() const
Is variable an array.
#define nonneg
Definition: config.h:138
@ warning
Warning.
@ portability
Portability warning.
@ style
Style warning.
@ information
Checking information.
@ performance
Performance warning.
@ error
Programming error.
std::string eitherTheConditionIsRedundant(const Token *condition)
Definition: valueflow.cpp:9680
Standards standards
Definition: library.h:171
Severity severity
Definition: library.h:172
std::string message
Definition: library.h:170
enum Standards::cstd_t c
enum Standards::cppstd_t cpp