Cppcheck
checkvaarg.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 #include "checkvaarg.h"
20 
21 #include "astutils.h"
22 #include "errortypes.h"
23 #include "settings.h"
24 #include "symboldatabase.h"
25 #include "token.h"
26 #include "tokenize.h"
27 
28 #include <cstddef>
29 #include <iterator>
30 #include <list>
31 #include <vector>
32 
33 //---------------------------------------------------------------------------
34 
35 // Register this check class (by creating a static instance of it)
36 namespace {
37  CheckVaarg instance;
38 }
39 
40 
41 //---------------------------------------------------------------------------
42 // Ensure that correct parameter is passed to va_start()
43 //---------------------------------------------------------------------------
44 
45 // CWE ids used:
46 static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime
47 static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument
48 static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
49 
51 {
52  const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
53  const std::size_t functions = symbolDatabase->functionScopes.size();
54  const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
55 
56  logChecker("CheckVaarg::va_start_argument");
57 
58  for (std::size_t i = 0; i < functions; ++i) {
59  const Scope* scope = symbolDatabase->functionScopes[i];
60  const Function* function = scope->function;
61  if (!function)
62  continue;
63  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
64  if (!tok->scope()->isExecutable())
65  tok = tok->scope()->bodyEnd;
66  else if (Token::simpleMatch(tok, "va_start (")) {
67  const Token* param2 = tok->tokAt(2)->nextArgument();
68  if (!param2)
69  continue;
70  const Variable* var = param2->variable();
71  if (var && var->isReference())
72  referenceAs_va_start_error(param2, var->name());
73  if (var && var->index() + 2 < function->argCount() && printWarnings) {
74  auto it = function->argumentList.end();
75  std::advance(it, -2);
76  wrongParameterTo_va_start_error(tok, var->name(), it->name()); // cppcheck-suppress derefInvalidIterator // FP due to isVariableChangedByFunctionCall()
77  }
78  tok = tok->linkAt(1);
79  }
80  }
81  }
82 }
83 
84 void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName)
85 {
87  "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, Certainty::normal);
88 }
89 
90 void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName)
91 {
93  "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, Certainty::normal);
94 }
95 
96 //---------------------------------------------------------------------------
97 // Detect missing va_end() if va_start() was used
98 // Detect va_list usage after va_end()
99 //---------------------------------------------------------------------------
100 
102 {
103  if (mSettings->clang)
104  return;
105 
106  logChecker("CheckVaarg::va_list_usage"); // notclang
107 
108  const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase();
109  for (const Variable* var : symbolDatabase->variableList()) {
110  if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list")
111  continue;
112  if (!var->isLocal() && !var->isArgument()) // Check only local variables and arguments
113  continue;
114 
115  bool open = var->isArgument(); // va_list passed as argument are opened
116  bool exitOnEndOfStatement = false;
117 
118  const Token* tok = var->nameToken()->next();
119  for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) {
120  // Skip lambdas
121  const Token* tok2 = findLambdaEndToken(tok);
122  if (tok2)
123  tok = tok2;
124  if (Token::Match(tok, "va_start ( %varid%", var->declarationId())) {
125  if (open)
126  va_start_subsequentCallsError(tok, var->name());
127  open = true;
128  tok = tok->linkAt(1);
129  } else if (Token::Match(tok, "va_end ( %varid%", var->declarationId())) {
130  if (!open)
131  va_list_usedBeforeStartedError(tok, var->name());
132  open = false;
133  tok = tok->linkAt(1);
134  } else if (Token::simpleMatch(tok, "va_copy (")) {
135  bool nopen = open;
136  if (tok->linkAt(1)->previous()->varId() == var->declarationId()) { // Source
137  if (!open)
138  va_list_usedBeforeStartedError(tok, var->name());
139  }
140  if (tok->tokAt(2)->varId() == var->declarationId()) { // Destination
141  if (open)
142  va_start_subsequentCallsError(tok, var->name());
143  nopen = true;
144  }
145  open = nopen;
146  tok = tok->linkAt(1);
147  } else if (Token::Match(tok, "throw|return"))
148  exitOnEndOfStatement = true;
149  else if (tok->str() == "break") {
150  tok = findNextTokenFromBreak(tok);
151  if (!tok)
152  return;
153  } else if (tok->str() == "goto" || (tok->isCpp() && tok->str() == "try")) {
154  open = false;
155  break;
156  } else if (!open && tok->varId() == var->declarationId())
157  va_list_usedBeforeStartedError(tok, var->name());
158  else if (exitOnEndOfStatement && tok->str() == ";")
159  break;
160  }
161  if (open && !var->isArgument())
162  va_end_missingError(tok, var->name());
163  }
164 }
165 
166 void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname)
167 {
169  "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, Certainty::normal);
170 }
171 
172 void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname)
173 {
175  "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, Certainty::normal);
176 }
177 
178 void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname)
179 {
181  "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, Certainty::normal);
182 }
const Token * findNextTokenFromBreak(const Token *breakToken)
For a "break" token, locate the next token to execute.
Definition: astutils.cpp:905
const Token * findLambdaEndToken(const Token *first)
find lambda function end token
Definition: astutils.cpp:3190
static const CWE CWE758(758U)
static const CWE CWE688(688U)
static const CWE CWE664(664U)
Checking for misusage of variable argument lists.
Definition: checkvaarg.h:42
void wrongParameterTo_va_start_error(const Token *tok, const std::string &paramIsName, const std::string &paramShouldName)
Definition: checkvaarg.cpp:84
void va_list_usedBeforeStartedError(const Token *tok, const std::string &varname)
Definition: checkvaarg.cpp:172
void referenceAs_va_start_error(const Token *tok, const std::string &paramName)
Definition: checkvaarg.cpp:90
void va_end_missingError(const Token *tok, const std::string &varname)
Definition: checkvaarg.cpp:166
void va_start_subsequentCallsError(const Token *tok, const std::string &varname)
Definition: checkvaarg.cpp:178
void va_list_usage()
Definition: checkvaarg.cpp:101
void va_start_argument()
Definition: checkvaarg.cpp:50
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
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
Function * function
function info for this function
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
bool clang
Use Clang.
Definition: settings.h:150
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
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 isCpp() const
Definition: token.cpp:2752
nonneg int varId() const
Definition: token.h:870
const Token * tokAt(int index) const
Definition: token.cpp:427
void scope(const Scope *s)
Associate this token with given scope.
Definition: token.h:1042
const Token * linkAt(int index) const
Definition: token.cpp:447
Token * previous()
Definition: token.h:862
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
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
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
Information about a member variable.
bool isReference() const
Is reference variable.
const std::string & name() const
Get name string.
nonneg int index() const
Get index of variable in declared order.
@ warning
Warning.
@ error
Programming error.