1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2021 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 //---------------------------------------------------------------------------
20 #include "checkio.h"
21 
22 #include "library.h"
23 #include "mathlib.h"
24 #include "settings.h"
25 #include "symboldatabase.h"
26 #include "token.h"
27 #include "tokenize.h"
28 #include "utils.h"
29 #include "valueflow.h"
30 
31 #include <cctype>
32 #include <cstdlib>
33 #include <list>
34 #include <map>
35 #include <set>
36 #include <utility>
37 #include <vector>
38 
39 //---------------------------------------------------------------------------
40 
41 // Register CheckIO..
42 namespace {
43     CheckIO instance;
44 }
45 
46 // CVE ID used:
47 static const CWE CWE119(119U);  // Improper Restriction of Operations within the Bounds of a Memory Buffer
48 static const CWE CWE398(398U);  // Indicator of Poor Code Quality
49 static const CWE CWE664(664U);  // Improper Control of a Resource Through its Lifetime
50 static const CWE CWE685(685U);  // Function Call With Incorrect Number of Arguments
51 static const CWE CWE686(686U);  // Function Call With Incorrect Argument Type
52 static const CWE CWE687(687U);  // Function Call With Incorrectly Specified Argument Value
53 static const CWE CWE704(704U);  // Incorrect Type Conversion or Cast
54 static const CWE CWE910(910U);  // Use of Expired File Descriptor
55 
56 //---------------------------------------------------------------------------
57 //    std::cout << std::cout;
58 //---------------------------------------------------------------------------
checkCoutCerrMisusage()59 void CheckIO::checkCoutCerrMisusage()
60 {
61     if (mTokenizer->isC())
62         return;
63 
64     const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
65     for (const Scope * scope : symbolDatabase->functionScopes) {
66         for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
67             if (Token::Match(tok, "std :: cout|cerr !!.") && tok->next()->astParent() && tok->next()->astParent()->astOperand1() == tok->next()) {
68                 const Token* tok2 = tok->next();
69                 while (tok2->astParent() && tok2->astParent()->str() == "<<") {
70                     tok2 = tok2->astParent();
71                     if (tok2->astOperand2() && Token::Match(tok2->astOperand2()->previous(), "std :: cout|cerr"))
72                         coutCerrMisusageError(tok, tok2->astOperand2()->strAt(1));
73                 }
74             }
75         }
76     }
77 }
78 
coutCerrMisusageError(const Token * tok,const std::string & streamName)79 void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName)
80 {
81     reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, Certainty::normal);
82 }
83 
84 //---------------------------------------------------------------------------
85 // fflush(stdin) <- fflush only applies to output streams in ANSI C
86 // fread(); fwrite(); <- consecutive read/write statements require repositioning in between
87 // fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
88 // fclose(); fread(); <- Use closed file
89 //---------------------------------------------------------------------------
90 enum class OpenMode { CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN_OM };
getMode(const std::string & str)91 static OpenMode getMode(const std::string& str)
92 {
93     if (str.find('+', 1) != std::string::npos)
94         return OpenMode::RW_MODE;
95     else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos)
96         return OpenMode::WRITE_MODE;
97     else if (str.find('r') != std::string::npos)
98         return OpenMode::READ_MODE;
99     return OpenMode::UNKNOWN_OM;
100 }
101 
102 struct Filepointer {
103     OpenMode mode;
104     nonneg int mode_indent;
105     enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation;
106     nonneg int op_indent;
107     enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
108     AppendMode append_mode;
109     std::string filename;
FilepointerFilepointer110     explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
111         : mode(mode_), mode_indent(0), lastOperation(Operation::NONE), op_indent(0), append_mode(AppendMode::UNKNOWN_AM) {}
112 };
113 
114 namespace {
115     const std::unordered_set<std::string> whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" };
116 }
117 
checkFileUsage()118 void CheckIO::checkFileUsage()
119 {
120     const bool windows = mSettings->isWindowsPlatform();
121     const bool printPortability = mSettings->severity.isEnabled(Severity::portability);
122     const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
123 
124     std::map<int, Filepointer> filepointers;
125 
126     const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
127     for (const Variable* var : symbolDatabase->variableList()) {
128         if (!var || !var->declarationId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *"))
129             continue;
130 
131         if (var->isLocal()) {
132             if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor"
133                 filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM)));
134             else
135                 filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::CLOSED)));
136         } else {
137             filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM)));
138             // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
139         }
140     }
141 
142     for (const Scope * scope : symbolDatabase->functionScopes) {
143         int indent = 0;
144         for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
145             if (tok->str() == "{")
146                 indent++;
147             else if (tok->str() == "}") {
148                 indent--;
149                 for (std::pair<const int, Filepointer>& filepointer : filepointers) {
150                     if (indent < filepointer.second.mode_indent) {
151                         filepointer.second.mode_indent = 0;
152                         filepointer.second.mode = OpenMode::UNKNOWN_OM;
153                     }
154                     if (indent < filepointer.second.op_indent) {
155                         filepointer.second.op_indent = 0;
156                         filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
157                     }
158                 }
159             } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break" || mSettings->library.isnoreturn(tok)) { // Reset upon return, continue or break
160                 for (std::pair<const int, Filepointer>& filepointer : filepointers) {
161                     filepointer.second.mode_indent = 0;
162                     filepointer.second.mode = OpenMode::UNKNOWN_OM;
163                     filepointer.second.op_indent = 0;
164                     filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
165                 }
166             } else if (Token::Match(tok, "%var% =") &&
167                        (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" &&
168                         (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) {
169                 std::map<int, Filepointer>::iterator i = filepointers.find(tok->varId());
170                 if (i != filepointers.end()) {
171                     i->second.mode = OpenMode::UNKNOWN_OM;
172                     i->second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
173                 }
174             } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
175                 std::string mode;
176                 const Token* fileTok = nullptr;
177                 const Token* fileNameTok = nullptr;
178                 Filepointer::Operation operation = Filepointer::Operation::NONE;
179 
180                 if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" ||
181                      (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) &&
182                     tok->strAt(-1) == "=") {
183                     if (tok->str() != "tmpfile") {
184                         const Token* modeTok = tok->tokAt(2)->nextArgument();
185                         if (modeTok && modeTok->tokType() == Token::eString)
186                             mode = modeTok->strValue();
187                     } else
188                         mode = "wb+";
189                     fileTok = tok->tokAt(-2);
190                     operation = Filepointer::Operation::OPEN;
191                     if (Token::Match(tok, "fopen ( %str% ,"))
192                         fileNameTok = tok->tokAt(2);
193                 } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) {
194                     const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument();
195                     if (modeTok && modeTok->tokType() == Token::eString)
196                         mode = modeTok->strValue();
197                     fileTok = tok->tokAt(3);
198                     operation = Filepointer::Operation::OPEN;
199                 } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") ||
200                            (windows && tok->str() == "_fseeki64")) {
201                     fileTok = tok->tokAt(2);
202                     if (printPortability && fileTok && tok->str() == "fflush") {
203                         if (fileTok->str() == "stdin")
204                             fflushOnInputStreamError(tok, fileTok->str());
205                         else {
206                             const Filepointer& f = filepointers[fileTok->varId()];
207                             if (f.mode == OpenMode::READ_MODE)
208                                 fflushOnInputStreamError(tok, fileTok->str());
209                         }
210                     }
211                     operation = Filepointer::Operation::POSITIONING;
212                 } else if (tok->str() == "fgetc" || tok->str() == "fgetwc" ||
213                            tok->str() == "fgets" || tok->str() == "fgetws" || tok->str() == "fread" ||
214                            tok->str() == "fscanf" || tok->str() == "fwscanf" || tok->str() == "getc" ||
215                            (windows && (tok->str() == "fscanf_s" || tok->str() == "fwscanf_s"))) {
216                     if (tok->str().find("scanf") != std::string::npos)
217                         fileTok = tok->tokAt(2);
218                     else
219                         fileTok = tok->linkAt(1)->previous();
220                     operation = Filepointer::Operation::READ;
221                 } else if (tok->str() == "fputc" || tok->str() == "fputwc" ||
222                            tok->str() == "fputs" || tok->str() == "fputws" || tok->str() == "fwrite" ||
223                            tok->str() == "fprintf" || tok->str() == "fwprintf" || tok->str() == "putcc" ||
224                            (windows && (tok->str() == "fprintf_s" || tok->str() == "fwprintf_s"))) {
225                     if (tok->str().find("printf") != std::string::npos)
226                         fileTok = tok->tokAt(2);
227                     else
228                         fileTok = tok->linkAt(1)->previous();
229                     operation = Filepointer::Operation::WRITE;
230                 } else if (tok->str() == "fclose") {
231                     fileTok = tok->tokAt(2);
232                     operation = Filepointer::Operation::CLOSE;
233                 } else if (whitelist.find(tok->str()) != whitelist.end()) {
234                     fileTok = tok->tokAt(2);
235                     if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok)
236                         fileTok = fileTok->nextArgument();
237                     operation = Filepointer::Operation::UNIMPORTANT;
238                 } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings->library.isFunctionConst(tok->str(), true)) {
239                     const Token* const end2 = tok->linkAt(1);
240                     if (scope->functionOf && scope->functionOf->isClassOrStruct() && !scope->function->isStatic() && ((tok->strAt(-1) != "::" && tok->strAt(-1) != ".") || tok->strAt(-2) == "this")) {
241                         if (!tok->function() || (tok->function()->nestedIn && tok->function()->nestedIn->isClassOrStruct())) {
242                             for (std::pair<const int, Filepointer>& filepointer : filepointers) {
243                                 const Variable* var = symbolDatabase->getVariableFromVarId(filepointer.first);
244                                 if (!var || !(var->isLocal() || var->isGlobal() || var->isStatic())) {
245                                     filepointer.second.mode = OpenMode::UNKNOWN_OM;
246                                     filepointer.second.mode_indent = 0;
247                                     filepointer.second.op_indent = indent;
248                                     filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
249                                 }
250                             }
251                             continue;
252                         }
253                     }
254                     for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
255                         if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) {
256                             fileTok = tok2;
257                             operation = Filepointer::Operation::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now
258                             break;
259                         }
260                     }
261                 }
262 
263                 while (Token::Match(fileTok, "%name% ."))
264                     fileTok = fileTok->tokAt(2);
265 
266                 if (!fileTok || !fileTok->varId() || fileTok->strAt(1) == "[")
267                     continue;
268 
269                 if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File
270                     filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM)));
271                 }
272 
273                 Filepointer& f = filepointers[fileTok->varId()];
274 
275                 switch (operation) {
276                 case Filepointer::Operation::OPEN:
277                     if (fileNameTok) {
278                         for (std::map<int, Filepointer>::const_iterator it = filepointers.cbegin(); it != filepointers.cend(); ++it) {
279                             const Filepointer &fptr = it->second;
280                             if (fptr.filename == fileNameTok->str() && (fptr.mode == OpenMode::RW_MODE || fptr.mode == OpenMode::WRITE_MODE))
281                                 incompatibleFileOpenError(tok, fileNameTok->str());
282                         }
283 
284                         f.filename = fileNameTok->str();
285                     }
286 
287                     f.mode = getMode(mode);
288                     if (mode.find('a') != std::string::npos) {
289                         if (f.mode == OpenMode::RW_MODE)
290                             f.append_mode = Filepointer::AppendMode::APPEND_EX;
291                         else
292                             f.append_mode = Filepointer::AppendMode::APPEND;
293                     } else
294                         f.append_mode = Filepointer::AppendMode::UNKNOWN_AM;
295                     f.mode_indent = indent;
296                     break;
297                 case Filepointer::Operation::POSITIONING:
298                     if (f.mode == OpenMode::CLOSED)
299                         useClosedFileError(tok);
300                     else if (f.append_mode == Filepointer::AppendMode::APPEND && tok->str() != "fflush" && printWarnings)
301                         seekOnAppendedFileError(tok);
302                     break;
303                 case Filepointer::Operation::READ:
304                     if (f.mode == OpenMode::CLOSED)
305                         useClosedFileError(tok);
306                     else if (f.mode == OpenMode::WRITE_MODE)
307                         readWriteOnlyFileError(tok);
308                     else if (f.lastOperation == Filepointer::Operation::WRITE)
309                         ioWithoutPositioningError(tok);
310                     break;
311                 case Filepointer::Operation::WRITE:
312                     if (f.mode == OpenMode::CLOSED)
313                         useClosedFileError(tok);
314                     else if (f.mode == OpenMode::READ_MODE)
315                         writeReadOnlyFileError(tok);
316                     else if (f.lastOperation == Filepointer::Operation::READ)
317                         ioWithoutPositioningError(tok);
318                     break;
319                 case Filepointer::Operation::CLOSE:
320                     if (f.mode == OpenMode::CLOSED)
321                         useClosedFileError(tok);
322                     else
323                         f.mode = OpenMode::CLOSED;
324                     f.mode_indent = indent;
325                     break;
326                 case Filepointer::Operation::UNIMPORTANT:
327                     if (f.mode == OpenMode::CLOSED)
328                         useClosedFileError(tok);
329                     break;
330                 case Filepointer::Operation::UNKNOWN_OP:
331                     f.mode = OpenMode::UNKNOWN_OM;
332                     f.mode_indent = 0;
333                     break;
334                 default:
335                     break;
336                 }
337                 if (operation != Filepointer::Operation::NONE && operation != Filepointer::Operation::UNIMPORTANT) {
338                     f.op_indent = indent;
339                     f.lastOperation = operation;
340                 }
341             }
342         }
343         for (std::pair<const int, Filepointer>& filepointer : filepointers) {
344             filepointer.second.op_indent = 0;
345             filepointer.second.mode = OpenMode::UNKNOWN_OM;
346             filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP;
347         }
348     }
349 }
350 
fflushOnInputStreamError(const Token * tok,const std::string & varname)351 void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname)
352 {
353     reportError(tok, Severity::portability,
354                 "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, Certainty::normal);
355 }
356 
ioWithoutPositioningError(const Token * tok)357 void CheckIO::ioWithoutPositioningError(const Token *tok)
358 {
359     reportError(tok, Severity::error,
360                 "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, Certainty::normal);
361 }
362 
readWriteOnlyFileError(const Token * tok)363 void CheckIO::readWriteOnlyFileError(const Token *tok)
364 {
365     reportError(tok, Severity::error,
366                 "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, Certainty::normal);
367 }
368 
writeReadOnlyFileError(const Token * tok)369 void CheckIO::writeReadOnlyFileError(const Token *tok)
370 {
371     reportError(tok, Severity::error,
372                 "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, Certainty::normal);
373 }
374 
useClosedFileError(const Token * tok)375 void CheckIO::useClosedFileError(const Token *tok)
376 {
377     reportError(tok, Severity::error,
378                 "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal);
379 }
380 
seekOnAppendedFileError(const Token * tok)381 void CheckIO::seekOnAppendedFileError(const Token *tok)
382 {
383     reportError(tok, Severity::warning,
384                 "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal);
385 }
386 
incompatibleFileOpenError(const Token * tok,const std::string & filename)387 void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename)
388 {
389     reportError(tok, Severity::warning,
390                 "incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal);
391 }
392 
393 
394 //---------------------------------------------------------------------------
395 // scanf without field width limits can crash with huge input data
396 //---------------------------------------------------------------------------
invalidScanf()397 void CheckIO::invalidScanf()
398 {
399     if (!mSettings->severity.isEnabled(Severity::warning))
400         return;
401 
402     const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase();
403     for (const Scope * scope : symbolDatabase->functionScopes) {
404         for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
405             const Token *formatToken = nullptr;
406             if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
407                 formatToken = tok->tokAt(2);
408             else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
409                 const Token* nextArg = tok->tokAt(2)->nextArgument();
410                 if (nextArg && nextArg->tokType() == Token::eString)
411                     formatToken = nextArg;
412                 else
413                     continue;
414             } else
415                 continue;
416 
417             bool format = false;
418 
419             // scan the string backwards, so we do not need to keep states
420             const std::string &formatstr(formatToken->str());
421             for (std::size_t i = 1; i < formatstr.length(); i++) {
422                 if (formatstr[i] == '%')
423                     format = !format;
424 
425                 else if (!format)
426                     continue;
427 
428                 else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') {
429                     format = false;
430                 }
431 
432                 else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') {
433                     if (formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's'))  // #3490 - field width limits are only necessary for string input
434                         invalidScanfError(tok);
435                     format = false;
436                 }
437             }
438         }
439     }
440 }
441 
invalidScanfError(const Token * tok)442 void CheckIO::invalidScanfError(const Token *tok)
443 {
444     const std::string fname = (tok ? tok->str() : std::string("scanf"));
445     reportError(tok, Severity::warning,
446                 "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" +
447                 fname + "() without field width limits can crash with huge input data. Add a field width "
448                 "specifier to fix this problem.\n"
449                 "\n"
450                 "Sample program that can crash:\n"
451                 "\n"
452                 "#include <stdio.h>\n"
453                 "int main()\n"
454                 "{\n"
455                 "    char c[5];\n"
456                 "    scanf(\"%s\", c);\n"
457                 "    return 0;\n"
458                 "}\n"
459                 "\n"
460                 "Typing in 5 or more characters may make the program crash. The correct usage "
461                 "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the "
462                 "terminating null byte.\n"
463                 "Source: http://linux.die.net/man/3/scanf\n"
464                 "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c",
465                 CWE119, Certainty::normal);
466 }
467 
468 //---------------------------------------------------------------------------
469 //    printf("%u", "xyz"); // Wrong argument type
470 //    printf("%u%s", 1); // Too few arguments
471 //    printf("", 1); // Too much arguments
472 //---------------------------------------------------------------------------
473 
findFormat(nonneg int arg,const Token * firstArg,const Token ** formatStringTok,const Token ** formatArgTok)474 static bool findFormat(nonneg int arg, const Token *firstArg,
475                        const Token **formatStringTok, const Token **formatArgTok)
476 {
477     const Token* argTok = firstArg;
478 
479     for (int i = 0; i < arg && argTok; ++i)
480         argTok = argTok->nextArgument();
481 
482     if (Token::Match(argTok, "%str% [,)]")) {
483         *formatArgTok = argTok->nextArgument();
484         *formatStringTok = argTok;
485         return true;
486     } else if (Token::Match(argTok, "%var% [,)]") &&
487                argTok->variable() &&
488                Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") &&
489                (argTok->variable()->isPointer() ||
490                 (argTok->variable()->dimensions().size() == 1 &&
491                  argTok->variable()->dimensionKnown(0) &&
492                  argTok->variable()->dimension(0) != 0))) {
493         *formatArgTok = argTok->nextArgument();
494         if (!argTok->values().empty()) {
495             std::list<ValueFlow::Value>::const_iterator value = std::find_if(
496                 argTok->values().begin(), argTok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue));
497             if (value != argTok->values().end() && value->isTokValue() && value->tokvalue &&
498                 value->tokvalue->tokType() == Token::eString) {
499                 *formatStringTok = value->tokvalue;
500             }
501         }
502         return true;
503     }
504     return false;
505 }
506 
507 // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename
typesMatch(const std::string & iToTest,const std::string & iTypename,const std::string & iOptionalPrefix="std::")508 static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::")
509 {
510     return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename);
511 }
512 
checkWrongPrintfScanfArguments()513 void CheckIO::checkWrongPrintfScanfArguments()
514 {
515     const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
516     const bool isWindows = mSettings->isWindowsPlatform();
517 
518     for (const Scope * scope : symbolDatabase->functionScopes) {
519         for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
520             if (!tok->isName()) continue;
521 
522             const Token* argListTok = nullptr; // Points to first va_list argument
523             const Token* formatStringTok = nullptr; // Points to format string token
524 
525             bool scan = false;
526             bool scanf_s = false;
527             int formatStringArgNo = -1;
528 
529             if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) {
530                 formatStringArgNo = mSettings->library.formatstr_argno(tok);
531                 scan = mSettings->library.formatstr_scan(tok);
532                 scanf_s = mSettings->library.formatstr_secure(tok);
533             }
534 
535             if (formatStringArgNo >= 0) {
536                 // formatstring found in library. Find format string and first argument belonging to format string.
537                 if (!findFormat(formatStringArgNo, tok->tokAt(2), &formatStringTok, &argListTok))
538                     continue;
539             } else if (Token::simpleMatch(tok, "swprintf (")) {
540                 if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) {
541                     // Find third parameter and format string
542                     if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok))
543                         continue;
544                 } else {
545                     // Find fourth parameter and format string
546                     if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok))
547                         continue;
548                 }
549             } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) {
550                 // template <size_t size> int sprintf_s(char (&buffer)[size], const char *format, ...);
551                 if (findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) {
552                     if (!formatStringTok)
553                         continue;
554                 }
555                 // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);
556                 else if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) {
557                     if (!formatStringTok)
558                         continue;
559                 }
560             } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) {
561                 // template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...);
562                 if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) {
563                     if (!formatStringTok)
564                         continue;
565                 }
566                 // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...);
567                 else if (findFormat(3, tok->tokAt(2), &formatStringTok, &argListTok)) {
568                     if (!formatStringTok)
569                         continue;
570                 }
571             } else {
572                 continue;
573             }
574 
575             if (!formatStringTok)
576                 continue;
577 
578             checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s);
579         }
580     }
581 }
582 
checkFormatString(const Token * const tok,const Token * const formatStringTok,const Token * argListTok,const bool scan,const bool scanf_s)583 void CheckIO::checkFormatString(const Token * const tok,
584                                 const Token * const formatStringTok,
585                                 const Token *       argListTok,
586                                 const bool scan,
587                                 const bool scanf_s)
588 {
589     const bool isWindows = mSettings->isWindowsPlatform();
590     const bool printWarning = mSettings->severity.isEnabled(Severity::warning);
591     const std::string &formatString = formatStringTok->str();
592 
593     // Count format string parameters..
594     int numFormat = 0;
595     int numSecure = 0;
596     bool percent = false;
597     const Token* argListTok2 = argListTok;
598     std::set<int> parameterPositionsUsed;
599     for (std::string::const_iterator i = formatString.begin(); i != formatString.end(); ++i) {
600         if (*i == '%') {
601             percent = !percent;
602         } else if (percent && *i == '[') {
603             while (i != formatString.end()) {
604                 if (*i == ']') {
605                     numFormat++;
606                     if (argListTok)
607                         argListTok = argListTok->nextArgument();
608                     percent = false;
609                     break;
610                 }
611                 ++i;
612             }
613             if (scanf_s) {
614                 numSecure++;
615                 if (argListTok) {
616                     argListTok = argListTok->nextArgument();
617                 }
618             }
619             if (i == formatString.end())
620                 break;
621         } else if (percent) {
622             percent = false;
623 
624             bool _continue = false;
625             bool skip = false;
626             std::string width;
627             int parameterPosition = 0;
628             bool hasParameterPosition = false;
629             while (i != formatString.end() && *i != '[' && !std::isalpha((unsigned char)*i)) {
630                 if (*i == '*') {
631                     skip = true;
632                     if (scan)
633                         _continue = true;
634                     else {
635                         numFormat++;
636                         if (argListTok)
637                             argListTok = argListTok->nextArgument();
638                     }
639                 } else if (std::isdigit(*i)) {
640                     width += *i;
641                 } else if (*i == '$') {
642                     parameterPosition = std::atoi(width.c_str());
643                     hasParameterPosition = true;
644                     width.clear();
645                 }
646                 ++i;
647             }
648             if (i != formatString.end() && *i == '[') {
649                 while (i != formatString.end()) {
650                     if (*i == ']') {
651                         if (!skip) {
652                             numFormat++;
653                             if (argListTok)
654                                 argListTok = argListTok->nextArgument();
655                         }
656                         break;
657                     }
658                     ++i;
659                 }
660                 if (scanf_s && !skip) {
661                     numSecure++;
662                     if (argListTok) {
663                         argListTok = argListTok->nextArgument();
664                     }
665                 }
666                 _continue = true;
667             }
668             if (i == formatString.end())
669                 break;
670             if (_continue)
671                 continue;
672 
673             if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
674                 ++numFormat;
675 
676                 // Handle parameter positions (POSIX extension) - Ticket #4900
677                 if (hasParameterPosition) {
678                     if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end())
679                         parameterPositionsUsed.insert(parameterPosition);
680                     else // Parameter already referenced, hence don't consider it a new format
681                         --numFormat;
682                 }
683 
684                 // Perform type checks
685                 ArgumentInfo argInfo(argListTok, mSettings, mTokenizer->isCPP());
686 
687                 if (argInfo.typeToken && !argInfo.isLibraryType(mSettings)) {
688                     if (scan) {
689                         std::string specifier;
690                         bool done = false;
691                         while (!done) {
692                             switch (*i) {
693                             case 's':
694                                 specifier += *i;
695                                 if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
696                                     if (!width.empty()) {
697                                         const int numWidth = std::atoi(width.c_str());
698                                         if (numWidth != (argInfo.variableInfo->dimension(0) - 1))
699                                             invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, 's');
700                                     }
701                                 }
702                                 if (argListTok && argListTok->tokType() != Token::eString &&
703                                     argInfo.isKnownType() && argInfo.isArrayOrPointer() &&
704                                     (!Token::Match(argInfo.typeToken, "char|wchar_t") ||
705                                      argInfo.typeToken->strAt(-1) == "const")) {
706                                     if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType()))
707                                         invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
708                                 }
709                                 if (scanf_s) {
710                                     numSecure++;
711                                     if (argListTok) {
712                                         argListTok = argListTok->nextArgument();
713                                     }
714                                 }
715                                 done = true;
716                                 break;
717                             case 'c':
718                                 if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) {
719                                     if (!width.empty()) {
720                                         const int numWidth = std::atoi(width.c_str());
721                                         if (numWidth > argInfo.variableInfo->dimension(0))
722                                             invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, 'c');
723                                     }
724                                 }
725                                 if (scanf_s) {
726                                     numSecure++;
727                                     if (argListTok) {
728                                         argListTok = argListTok->nextArgument();
729                                     }
730                                 }
731                                 done = true;
732                                 break;
733                             case 'x':
734                             case 'X':
735                             case 'u':
736                             case 'o':
737                                 specifier += *i;
738                                 if (argInfo.typeToken->tokType() == Token::eString)
739                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
740                                 else if (argInfo.isKnownType()) {
741                                     if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
742                                         if (argInfo.typeToken->isStandardType() || !argInfo.element)
743                                             invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
744                                     } else if (!argInfo.typeToken->isUnsigned() ||
745                                                !argInfo.isArrayOrPointer() ||
746                                                argInfo.typeToken->strAt(-1) == "const") {
747                                         invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
748                                     } else {
749                                         switch (specifier[0]) {
750                                         case 'h':
751                                             if (specifier[1] == 'h') {
752                                                 if (argInfo.typeToken->str() != "char")
753                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
754                                             } else if (argInfo.typeToken->str() != "short")
755                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
756                                             break;
757                                         case 'l':
758                                             if (specifier[1] == 'l') {
759                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
760                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
761                                                 else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
762                                                          typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
763                                                          typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
764                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
765                                             } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
766                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
767                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
768                                                      typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
769                                                      typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
770                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
771                                             break;
772                                         case 'I':
773                                             if (specifier.find("I64") != std::string::npos) {
774                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
775                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
776                                             } else if (specifier.find("I32") != std::string::npos) {
777                                                 if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
778                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
779                                             } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
780                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
781                                             break;
782                                         case 'j':
783                                             if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
784                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
785                                             break;
786                                         case 'z':
787                                             if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
788                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
789                                             break;
790                                         case 't':
791                                             if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
792                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
793                                             break;
794                                         case 'L':
795                                             if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
796                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
797                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
798                                                      typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
799                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
800                                             break;
801                                         default:
802                                             if (argInfo.typeToken->str() != "int")
803                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
804                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
805                                                      typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
806                                                      typesMatch(argInfo.typeToken->originalName(), "uintmax_t"))
807                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
808                                             break;
809                                         }
810                                     }
811                                 }
812                                 done = true;
813                                 break;
814                             case 'n':
815                             case 'd':
816                             case 'i':
817                                 specifier += *i;
818                                 if (argInfo.typeToken->tokType() == Token::eString)
819                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
820                                 else if (argInfo.isKnownType()) {
821                                     if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
822                                         if (argInfo.typeToken->isStandardType() || !argInfo.element)
823                                             invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
824                                     } else if (argInfo.typeToken->isUnsigned() ||
825                                                !argInfo.isArrayOrPointer() ||
826                                                argInfo.typeToken->strAt(-1) == "const") {
827                                         invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
828                                     } else {
829                                         switch (specifier[0]) {
830                                         case 'h':
831                                             if (specifier[1] == 'h') {
832                                                 if (argInfo.typeToken->str() != "char")
833                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
834                                             } else if (argInfo.typeToken->str() != "short")
835                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
836                                             break;
837                                         case 'l':
838                                             if (specifier[1] == 'l') {
839                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
840                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
841                                                 else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
842                                                          typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
843                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
844                                             } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
845                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
846                                             else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
847                                                      typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
848                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
849                                             break;
850                                         case 'I':
851                                             if (specifier.find("I64") != std::string::npos) {
852                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
853                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
854                                             } else if (specifier.find("I32") != std::string::npos) {
855                                                 if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
856                                                     invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
857                                             } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
858                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
859                                             break;
860                                         case 'j':
861                                             if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t"))
862                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
863                                             break;
864                                         case 'z':
865                                             if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
866                                                   (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
867                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
868                                             break;
869                                         case 't':
870                                             if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
871                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
872                                             break;
873                                         case 'L':
874                                             if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
875                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
876                                             break;
877                                         default:
878                                             if (argInfo.typeToken->str() != "int")
879                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
880                                             else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
881                                                      argInfo.typeToken->originalName() == "intmax_t")
882                                                 invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
883                                             break;
884                                         }
885                                     }
886                                 }
887                                 done = true;
888                                 break;
889                             case 'e':
890                             case 'E':
891                             case 'f':
892                             case 'g':
893                             case 'G':
894                             case 'a':
895                                 specifier += *i;
896                                 if (argInfo.typeToken->tokType() == Token::eString)
897                                     invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
898                                 else if (argInfo.isKnownType()) {
899                                     if (!Token::Match(argInfo.typeToken, "float|double")) {
900                                         if (argInfo.typeToken->isStandardType())
901                                             invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
902                                     } else if (!argInfo.isArrayOrPointer() ||
903                                                argInfo.typeToken->strAt(-1) == "const") {
904                                         invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
905                                     } else {
906                                         switch (specifier[0]) {
907                                         case 'l':
908                                             if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong())
909                                                 invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
910                                             break;
911                                         case 'L':
912                                             if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong())
913                                                 invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
914                                             break;
915                                         default:
916                                             if (argInfo.typeToken->str() != "float")
917                                                 invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
918                                             break;
919                                         }
920                                     }
921                                 }
922                                 done = true;
923                                 break;
924                             case 'I':
925                                 if ((i+1 != formatString.end() && *(i+1) == '6' &&
926                                      i+2 != formatString.end() && *(i+2) == '4') ||
927                                     (i+1 != formatString.end() && *(i+1) == '3' &&
928                                      i+2 != formatString.end() && *(i+2) == '2')) {
929                                     specifier += *i++;
930                                     specifier += *i++;
931                                     if ((i+1) != formatString.end() && !isalpha(*(i+1))) {
932                                         specifier += *i;
933                                         invalidLengthModifierError(tok, numFormat, specifier);
934                                         done = true;
935                                     } else {
936                                         specifier += *i++;
937                                     }
938                                 } else {
939                                     if ((i+1) != formatString.end() && !isalpha(*(i+1))) {
940                                         specifier += *i;
941                                         invalidLengthModifierError(tok, numFormat, specifier);
942                                         done = true;
943                                     } else {
944                                         specifier += *i++;
945                                     }
946                                 }
947                                 break;
948                             case 'h':
949                             case 'l':
950                                 if (i+1 != formatString.end() && *(i+1) == *i)
951                                     specifier += *i++;
952                                 FALLTHROUGH;
953                             case 'j':
954                             case 'q':
955                             case 't':
956                             case 'z':
957                             case 'L':
958                                 // Expect an alphabetical character after these specifiers
959                                 if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
960                                     specifier += *i;
961                                     invalidLengthModifierError(tok, numFormat, specifier);
962                                     done = true;
963                                 } else {
964                                     specifier += *i++;
965                                 }
966                                 break;
967                             default:
968                                 done = true;
969                                 break;
970                             }
971                         }
972                     } else if (printWarning) {
973                         std::string specifier;
974                         bool done = false;
975                         while (!done) {
976                             switch (*i) {
977                             case 's':
978                                 if (argListTok->tokType() != Token::eString &&
979                                     argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
980                                     if (!Token::Match(argInfo.typeToken, "char|wchar_t")) {
981                                         if (!argInfo.element)
982                                             invalidPrintfArgTypeError_s(tok, numFormat, &argInfo);
983                                     }
984                                 }
985                                 done = true;
986                                 break;
987                             case 'n':
988                                 if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString)
989                                     invalidPrintfArgTypeError_n(tok, numFormat, &argInfo);
990                                 done = true;
991                                 break;
992                             case 'c':
993                             case 'x':
994                             case 'X':
995                             case 'o':
996                                 specifier += *i;
997                                 if (argInfo.typeToken->tokType() == Token::eString)
998                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
999                                 else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1000                                     // use %p on pointers and arrays
1001                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1002                                 } else if (argInfo.isKnownType()) {
1003                                     if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) {
1004                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1005                                             invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1006                                     } else {
1007                                         switch (specifier[0]) {
1008                                         case 'h':
1009                                             if (specifier[1] == 'h') {
1010                                                 if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1011                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1012                                             } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1013                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1014                                             break;
1015                                         case 'l':
1016                                             if (specifier[1] == 'l') {
1017                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1018                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1019                                                 else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1020                                                          argInfo.typeToken->originalName() == "uintmax_t")
1021                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1022                                             } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1023                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1024                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1025                                                      argInfo.typeToken->originalName() == "uintmax_t")
1026                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1027                                             break;
1028                                         case 'j':
1029                                             if (argInfo.typeToken->originalName() != "uintmax_t")
1030                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1031                                             break;
1032                                         case 'z':
1033                                             if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1034                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1035                                             break;
1036                                         case 't':
1037                                             if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1038                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1039                                             break;
1040                                         case 'I':
1041                                             if (specifier.find("I64") != std::string::npos) {
1042                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1043                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1044                                             } else if (specifier.find("I32") != std::string::npos) {
1045                                                 if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1046                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1047                                             } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1048                                                          argInfo.typeToken->originalName() == "WPARAM" ||
1049                                                          argInfo.typeToken->originalName() == "UINT_PTR" ||
1050                                                          argInfo.typeToken->originalName() == "LONG_PTR" ||
1051                                                          argInfo.typeToken->originalName() == "LPARAM" ||
1052                                                          argInfo.typeToken->originalName() == "LRESULT"))
1053                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1054                                             break;
1055                                         default:
1056                                             if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int"))
1057                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1058                                             break;
1059                                         }
1060                                     }
1061                                 }
1062                                 done = true;
1063                                 break;
1064                             case 'd':
1065                             case 'i':
1066                                 specifier += *i;
1067                                 if (argInfo.typeToken->tokType() == Token::eString) {
1068                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1069                                 } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1070                                     // use %p on pointers and arrays
1071                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1072                                 } else if (argInfo.isKnownType()) {
1073                                     if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) {
1074                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1075                                             invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1076                                     } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) {
1077                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1078                                             invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1079                                     } else {
1080                                         switch (specifier[0]) {
1081                                         case 'h':
1082                                             if (specifier[1] == 'h') {
1083                                                 if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned()))
1084                                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1085                                             } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned()))
1086                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1087                                             break;
1088                                         case 'l':
1089                                             if (specifier[1] == 'l') {
1090                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1091                                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1092                                                 else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1093                                                          argInfo.typeToken->originalName() == "intmax_t")
1094                                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1095                                             } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1096                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1097                                             else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1098                                                      argInfo.typeToken->originalName() == "intmax_t")
1099                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1100                                             break;
1101                                         case 'j':
1102                                             if (argInfo.typeToken->originalName() != "intmax_t")
1103                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1104                                             break;
1105                                         case 't':
1106                                             if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1107                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1108                                             break;
1109                                         case 'I':
1110                                             if (specifier.find("I64") != std::string::npos) {
1111                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1112                                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1113                                             } else if (specifier.find("I32") != std::string::npos) {
1114                                                 if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1115                                                     invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1116                                             } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1117                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1118                                             break;
1119                                         case 'z':
1120                                             if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") ||
1121                                                   (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T"))))
1122                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1123                                             break;
1124                                         case 'L':
1125                                             if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1126                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1127                                             break;
1128                                         default:
1129                                             if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1130                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1131                                             else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") ||
1132                                                      argInfo.typeToken->originalName() == "intmax_t")
1133                                                 invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
1134                                             break;
1135                                         }
1136                                     }
1137                                 }
1138                                 done = true;
1139                                 break;
1140                             case 'u':
1141                                 specifier += *i;
1142                                 if (argInfo.typeToken->tokType() == Token::eString) {
1143                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1144                                 } else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1145                                     // use %p on pointers and arrays
1146                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1147                                 } else if (argInfo.isKnownType()) {
1148                                     if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) {
1149                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1150                                             invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1151                                     } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) {
1152                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1153                                             invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1154                                     } else {
1155                                         switch (specifier[0]) {
1156                                         case 'h':
1157                                             if (specifier[1] == 'h') {
1158                                                 if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned()))
1159                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1160                                             } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned()))
1161                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1162                                             break;
1163                                         case 'l':
1164                                             if (specifier[1] == 'l') {
1165                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1166                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1167                                                 else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1168                                                          argInfo.typeToken->originalName() == "uintmax_t")
1169                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1170                                             } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong())
1171                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1172                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1173                                                      argInfo.typeToken->originalName() == "uintmax_t")
1174                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1175                                             break;
1176                                         case 'j':
1177                                             if (argInfo.typeToken->originalName() != "uintmax_t")
1178                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1179                                             break;
1180                                         case 'z':
1181                                             if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1182                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1183                                             break;
1184                                         case 't':
1185                                             if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t"))
1186                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1187                                             break;
1188                                         case 'I':
1189                                             if (specifier.find("I64") != std::string::npos) {
1190                                                 if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1191                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1192                                             } else if (specifier.find("I32") != std::string::npos) {
1193                                                 if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
1194                                                     invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1195                                             } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t"))
1196                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1197                                             break;
1198                                         case 'L':
1199                                             if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
1200                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1201                                             break;
1202                                         default:
1203                                             if (!Token::Match(argInfo.typeToken, "bool|char|short|int"))
1204                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1205                                             else if (typesMatch(argInfo.typeToken->originalName(), "size_t") ||
1206                                                      argInfo.typeToken->originalName() == "intmax_t")
1207                                                 invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
1208                                             break;
1209                                         }
1210                                     }
1211                                 }
1212                                 done = true;
1213                                 break;
1214                             case 'p':
1215                                 if (argInfo.typeToken->tokType() == Token::eString)
1216                                     ; // string literals are passed as pointers to literal start, okay
1217                                 else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer())
1218                                     invalidPrintfArgTypeError_p(tok, numFormat, &argInfo);
1219                                 done = true;
1220                                 break;
1221                             case 'e':
1222                             case 'E':
1223                             case 'f':
1224                             case 'g':
1225                             case 'G':
1226                                 specifier += *i;
1227                                 if (argInfo.typeToken->tokType() == Token::eString)
1228                                     invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1229                                 else if (argInfo.isArrayOrPointer() && !argInfo.element) {
1230                                     // use %p on pointers and arrays
1231                                     invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1232                                 } else if (argInfo.isKnownType()) {
1233                                     if (!Token::Match(argInfo.typeToken, "float|double")) {
1234                                         if (!(!argInfo.isArrayOrPointer() && argInfo.element))
1235                                             invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1236                                     } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
1237                                                (specifier[0] != 'L' && argInfo.typeToken->isLong()))
1238                                         invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
1239                                 }
1240                                 done = true;
1241                                 break;
1242                             case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
1243                             case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
1244                                 // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
1245                                 if (i != formatString.end() && (i + 1) != formatString.end() && *(i + 1) == *i) {
1246                                     if ((i + 2) != formatString.end() && !isalpha(*(i + 2))) {
1247                                         std::string modifier;
1248                                         modifier += *i;
1249                                         modifier += *(i + 1);
1250                                         invalidLengthModifierError(tok, numFormat, modifier);
1251                                         done = true;
1252                                     } else {
1253                                         specifier = *i++;
1254                                         specifier += *i++;
1255                                     }
1256                                 } else {
1257                                     if (i != formatString.end()) {
1258                                         if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) {
1259                                             std::string modifier;
1260                                             modifier += *i;
1261                                             invalidLengthModifierError(tok, numFormat, modifier);
1262                                             done = true;
1263                                         } else {
1264                                             specifier = *i++;
1265                                         }
1266                                     } else {
1267                                         done = true;
1268                                     }
1269                                 }
1270                             }
1271                             break;
1272                             case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64
1273                                 if ((*(i+1) == '3' && *(i+2) == '2') ||
1274                                     (*(i+1) == '6' && *(i+2) == '4')) {
1275                                     specifier += *i++;
1276                                     specifier += *i++;
1277                                 }
1278                                 FALLTHROUGH;
1279                             case 'j': // intmax_t or uintmax_t
1280                             case 'z': // size_t
1281                             case 't': // ptrdiff_t
1282                             case 'L': // long double
1283                                 // Expect an alphabetical character after these specifiers
1284                                 if ((i + 1) != formatString.end() && !isalpha(*(i+1))) {
1285                                     specifier += *i;
1286                                     invalidLengthModifierError(tok, numFormat, specifier);
1287                                     done = true;
1288                                 } else {
1289                                     specifier += *i++;
1290                                 }
1291                                 break;
1292                             default:
1293                                 done = true;
1294                                 break;
1295                             }
1296                         }
1297                     }
1298                 }
1299 
1300                 if (argListTok)
1301                     argListTok = argListTok->nextArgument(); // Find next argument
1302             }
1303         }
1304     }
1305 
1306     // Count printf/scanf parameters..
1307     int numFunction = 0;
1308     while (argListTok2) {
1309         numFunction++;
1310         argListTok2 = argListTok2->nextArgument(); // Find next argument
1311     }
1312 
1313     if (printWarning) {
1314         // Check that all parameter positions reference an actual parameter
1315         for (int i : parameterPositionsUsed) {
1316             if ((i == 0) || (i > numFormat))
1317                 wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat);
1318         }
1319     }
1320 
1321     // Mismatching number of parameters => warning
1322     if ((numFormat + numSecure) != numFunction)
1323         wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction);
1324 }
1325 
1326 // We currently only support string literals, variables, and functions.
1327 /// @todo add non-string literals, and generic expressions
1328 
ArgumentInfo(const Token * arg,const Settings * settings,bool _isCPP)1329 CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP)
1330     : variableInfo(nullptr)
1331     , typeToken(nullptr)
1332     , functionInfo(nullptr)
1333     , tempToken(nullptr)
1334     , element(false)
1335     , _template(false)
1336     , address(false)
1337     , isCPP(_isCPP)
1338 {
1339     if (!arg)
1340         return;
1341 
1342     // Use AST type info
1343     // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type.
1344     if (!Token::Match(arg, "%str% ,|)") && !(Token::Match(arg,"%var%") && arg->variable() && arg->variable()->isArray())) {
1345         const Token *top = arg;
1346         while (top->str() == "(" && !top->isCast())
1347             top = top->next();
1348         while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous())
1349             top = top->astParent();
1350         const ValueType *valuetype = top->argumentType();
1351         if (valuetype && valuetype->type >= ValueType::Type::BOOL) {
1352             typeToken = tempToken = new Token();
1353             if (valuetype->pointer && valuetype->constness & 1) {
1354                 tempToken->str("const");
1355                 tempToken->insertToken("a");
1356                 tempToken = tempToken->next();
1357             }
1358             if (valuetype->type == ValueType::BOOL)
1359                 tempToken->str("bool");
1360             else if (valuetype->type == ValueType::CHAR)
1361                 tempToken->str("char");
1362             else if (valuetype->type == ValueType::SHORT)
1363                 tempToken->str("short");
1364             else if (valuetype->type == ValueType::WCHAR_T)
1365                 tempToken->str("wchar_t");
1366             else if (valuetype->type == ValueType::INT)
1367                 tempToken->str("int");
1368             else if (valuetype->type == ValueType::LONG)
1369                 tempToken->str("long");
1370             else if (valuetype->type == ValueType::LONGLONG) {
1371                 tempToken->str("long");
1372                 tempToken->isLong(true);
1373             } else if (valuetype->type == ValueType::FLOAT)
1374                 tempToken->str("float");
1375             else if (valuetype->type == ValueType::DOUBLE)
1376                 tempToken->str("double");
1377             else if (valuetype->type == ValueType::LONGDOUBLE) {
1378                 tempToken->str("double");
1379                 tempToken->isLong(true);
1380             }
1381             if (valuetype->isIntegral()) {
1382                 if (valuetype->sign == ValueType::Sign::UNSIGNED)
1383                     tempToken->isUnsigned(true);
1384                 else if (valuetype->sign == ValueType::Sign::SIGNED)
1385                     tempToken->isSigned(true);
1386             }
1387             if (!valuetype->originalTypeName.empty())
1388                 tempToken->originalName(valuetype->originalTypeName);
1389             for (int p = 0; p < valuetype->pointer; p++)
1390                 tempToken->insertToken("*");
1391             tempToken = const_cast<Token*>(typeToken);
1392             return;
1393         }
1394     }
1395 
1396 
1397     if (arg->tokType() == Token::eString) {
1398         typeToken = arg;
1399         return;
1400     } else if (arg->str() == "&" || arg->tokType() == Token::eVariable ||
1401                arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") ||
1402                (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") &&
1403                 Token::simpleMatch(arg->linkAt(1), "> (") &&
1404                 Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) {
1405         if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) {
1406             typeToken = arg->tokAt(2);
1407             while (typeToken->str() == "const" || typeToken->str() == "extern")
1408                 typeToken = typeToken->next();
1409             return;
1410         }
1411         if (arg->str() == "&") {
1412             address = true;
1413             arg = arg->next();
1414         }
1415         while (Token::Match(arg, "%type% ::"))
1416             arg = arg->tokAt(2);
1417         if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction))
1418             return;
1419         const Token *varTok = nullptr;
1420         const Token *tok1 = arg->next();
1421         for (; tok1; tok1 = tok1->next()) {
1422             if (tok1->str() == "," || tok1->str() == ")") {
1423                 if (tok1->previous()->str() == "]") {
1424                     varTok = tok1->linkAt(-1)->previous();
1425                     if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) {
1426                         const Function * function = varTok->link()->previous()->function();
1427                         if (function && function->retType && function->retType->isEnumType()) {
1428                             if (function->retType->classScope->enumType)
1429                                 typeToken = function->retType->classScope->enumType;
1430                             else {
1431                                 tempToken = new Token();
1432                                 tempToken->fileIndex(tok1->fileIndex());
1433                                 tempToken->linenr(tok1->linenr());
1434                                 tempToken->str("int");
1435                                 typeToken = tempToken;
1436                             }
1437                         } else if (function && function->retDef) {
1438                             typeToken = function->retDef;
1439                             while (typeToken->str() == "const" || typeToken->str() == "extern")
1440                                 typeToken = typeToken->next();
1441                             functionInfo = function;
1442                             element = true;
1443                         }
1444                         return;
1445                     }
1446                 } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) {
1447                     const Function * function = tok1->linkAt(-1)->previous()->function();
1448                     if (function && function->retType && function->retType->isEnumType()) {
1449                         if (function->retType->classScope->enumType)
1450                             typeToken = function->retType->classScope->enumType;
1451                         else {
1452                             tempToken = new Token();
1453                             tempToken->fileIndex(tok1->fileIndex());
1454                             tempToken->linenr(tok1->linenr());
1455                             tempToken->str("int");
1456                             typeToken = tempToken;
1457                         }
1458                     } else if (function && function->retDef) {
1459                         typeToken = function->retDef;
1460                         while (typeToken->str() == "const" || typeToken->str() == "extern")
1461                             typeToken = typeToken->next();
1462                         functionInfo = function;
1463                         element = false;
1464                     }
1465                     return;
1466                 } else
1467                     varTok = tok1->previous();
1468                 break;
1469             } else if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[")
1470                 tok1 = tok1->link();
1471             else if (tok1->link() && tok1->str() == "<")
1472                 tok1 = tok1->link();
1473 
1474             // check for some common well known functions
1475             else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) ||
1476                                (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) {
1477                 tempToken = new Token();
1478                 tempToken->fileIndex(tok1->fileIndex());
1479                 tempToken->linenr(tok1->linenr());
1480                 if (tok1->next()->str() == "size") {
1481                     // size_t is platform dependent
1482                     if (settings->sizeof_size_t == 8) {
1483                         tempToken->str("long");
1484                         if (settings->sizeof_long != 8)
1485                             tempToken->isLong(true);
1486                     } else if (settings->sizeof_size_t == 4) {
1487                         if (settings->sizeof_long == 4) {
1488                             tempToken->str("long");
1489                         } else {
1490                             tempToken->str("int");
1491                         }
1492                     }
1493 
1494                     tempToken->originalName("size_t");
1495                     tempToken->isUnsigned(true);
1496                 } else if (tok1->next()->str() == "empty") {
1497                     tempToken->str("bool");
1498                 } else if (tok1->next()->str() == "c_str") {
1499                     tempToken->str("const");
1500                     tempToken->insertToken("*");
1501                     if (typeToken->strAt(2) == "string")
1502                         tempToken->insertToken("char");
1503                     else
1504                         tempToken->insertToken("wchar_t");
1505                 }
1506                 typeToken = tempToken;
1507                 return;
1508             }
1509 
1510             // check for std::vector::at() and std::string::at()
1511             else if (Token::Match(tok1->previous(), "%var% . at (") &&
1512                      Token::Match(tok1->linkAt(2), ") [,)]")) {
1513                 varTok = tok1->previous();
1514                 variableInfo = varTok->variable();
1515 
1516                 if (!variableInfo || !isStdVectorOrString()) {
1517                     variableInfo = nullptr;
1518                     typeToken = nullptr;
1519                 }
1520 
1521                 return;
1522             } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction))
1523                 return;
1524         }
1525 
1526         if (varTok) {
1527             variableInfo = varTok->variable();
1528             element = tok1->previous()->str() == "]";
1529 
1530             // look for std::vector operator [] and use template type as return type
1531             if (variableInfo) {
1532                 if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true
1533                     element = false;    // not really an array element
1534                 } else if (variableInfo->isEnumType()) {
1535                     if (variableInfo->type() && variableInfo->type()->classScope && variableInfo->type()->classScope->enumType)
1536                         typeToken = variableInfo->type()->classScope->enumType;
1537                     else {
1538                         tempToken = new Token();
1539                         tempToken->fileIndex(tok1->fileIndex());
1540                         tempToken->linenr(tok1->linenr());
1541                         tempToken->str("int");
1542                         typeToken = tempToken;
1543                     }
1544                 } else
1545                     typeToken = variableInfo->typeStartToken();
1546             }
1547 
1548             return;
1549         }
1550     }
1551 }
1552 
~ArgumentInfo()1553 CheckIO::ArgumentInfo::~ArgumentInfo()
1554 {
1555     if (tempToken) {
1556         while (tempToken->next())
1557             tempToken->deleteNext();
1558 
1559         delete tempToken;
1560     }
1561 }
1562 
1563 namespace {
1564     const std::set<std::string> stl_vector = { "array", "vector" };
1565     const std::set<std::string> stl_string = { "string", "u16string", "u32string", "wstring" };
1566 }
1567 
isStdVectorOrString()1568 bool CheckIO::ArgumentInfo::isStdVectorOrString()
1569 {
1570     if (!isCPP)
1571         return false;
1572     if (variableInfo->isStlType(stl_vector)) {
1573         typeToken = variableInfo->typeStartToken()->tokAt(4);
1574         _template = true;
1575         return true;
1576     } else if (variableInfo->isStlType(stl_string)) {
1577         tempToken = new Token();
1578         tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex());
1579         tempToken->linenr(variableInfo->typeStartToken()->linenr());
1580         if (variableInfo->typeStartToken()->strAt(2) == "string")
1581             tempToken->str("char");
1582         else
1583             tempToken->str("wchar_t");
1584         typeToken = tempToken;
1585         return true;
1586     } else if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) {
1587         const std::vector<Type::BaseInfo>& derivedFrom = variableInfo->type()->derivedFrom;
1588         for (const Type::BaseInfo & i : derivedFrom) {
1589             const Token* nameTok = i.nameTok;
1590             if (Token::Match(nameTok, "std :: vector|array <")) {
1591                 typeToken = nameTok->tokAt(4);
1592                 _template = true;
1593                 return true;
1594             } else if (Token::Match(nameTok, "std :: string|wstring")) {
1595                 tempToken = new Token();
1596                 tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex());
1597                 tempToken->linenr(variableInfo->typeStartToken()->linenr());
1598                 if (nameTok->strAt(2) == "string")
1599                     tempToken->str("char");
1600                 else
1601                     tempToken->str("wchar_t");
1602                 typeToken = tempToken;
1603                 return true;
1604             }
1605         }
1606     } else if (variableInfo->type()) {
1607         const Scope * classScope = variableInfo->type()->classScope;
1608         if (classScope) {
1609             for (const Function &func : classScope->functionList) {
1610                 if (func.name() == "operator[]") {
1611                     if (Token::Match(func.retDef, "%type% &")) {
1612                         typeToken = func.retDef;
1613                         return true;
1614                     }
1615                 }
1616             }
1617         }
1618     }
1619 
1620     return false;
1621 }
1622 
1623 static const std::set<std::string> stl_container = {
1624     "array", "bitset", "deque", "forward_list",
1625     "hash_map", "hash_multimap", "hash_set",
1626     "list", "map", "multimap", "multiset",
1627     "priority_queue", "queue", "set", "stack",
1628     "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector"
1629 };
1630 
isStdContainer(const Token * tok)1631 bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok)
1632 {
1633     if (!isCPP)
1634         return false;
1635     if (tok && tok->variable()) {
1636         const Variable* variable = tok->variable();
1637         if (variable->isStlType(stl_container)) {
1638             typeToken = variable->typeStartToken()->tokAt(4);
1639             return true;
1640         } else if (variable->isStlType(stl_string)) {
1641             typeToken = variable->typeStartToken();
1642             return true;
1643         } else if (variable->type() && !variable->type()->derivedFrom.empty()) {
1644             for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) {
1645                 const Token* nameTok = baseInfo.nameTok;
1646                 if (Token::Match(nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) {
1647                     typeToken = nameTok->tokAt(4);
1648                     return true;
1649                 } else if (Token::Match(nameTok, "std :: string|wstring")) {
1650                     typeToken = nameTok;
1651                     return true;
1652                 }
1653             }
1654         }
1655     }
1656 
1657     return false;
1658 }
1659 
isArrayOrPointer() const1660 bool CheckIO::ArgumentInfo::isArrayOrPointer() const
1661 {
1662     if (address)
1663         return true;
1664     else if (variableInfo && !_template) {
1665         return variableInfo->isArrayOrPointer();
1666     } else {
1667         const Token *tok = typeToken;
1668         while (Token::Match(tok, "const|struct"))
1669             tok = tok->next();
1670         if (tok && tok->strAt(1) == "*")
1671             return true;
1672     }
1673     return false;
1674 }
1675 
isComplexType() const1676 bool CheckIO::ArgumentInfo::isComplexType() const
1677 {
1678     if (variableInfo->type())
1679         return (true);
1680 
1681     const Token* varTypeTok = typeToken;
1682     if (varTypeTok->str() == "std")
1683         varTypeTok = varTypeTok->tokAt(2);
1684 
1685     return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer());
1686 }
1687 
isKnownType() const1688 bool CheckIO::ArgumentInfo::isKnownType() const
1689 {
1690     if (variableInfo)
1691         return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType());
1692     else if (functionInfo)
1693         return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring"));
1694 
1695     return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring");
1696 }
1697 
isLibraryType(const Settings * settings) const1698 bool CheckIO::ArgumentInfo::isLibraryType(const Settings *settings) const
1699 {
1700     return typeToken && typeToken->isStandardType() && settings->library.podtype(typeToken->str());
1701 }
1702 
wrongPrintfScanfArgumentsError(const Token * tok,const std::string & functionName,nonneg int numFormat,nonneg int numFunction)1703 void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
1704                                              const std::string &functionName,
1705                                              nonneg int numFormat,
1706                                              nonneg int numFunction)
1707 {
1708     const Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning;
1709     if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning))
1710         return;
1711 
1712     std::ostringstream errmsg;
1713     errmsg << functionName
1714            << " format string requires "
1715            << numFormat
1716            << " parameter" << (numFormat != 1 ? "s" : "") << " but "
1717            << (numFormat > numFunction ? "only " : "")
1718            << numFunction
1719            << (numFunction != 1 ? " are" : " is")
1720            << " given.";
1721 
1722     reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal);
1723 }
1724 
wrongPrintfScanfPosixParameterPositionError(const Token * tok,const std::string & functionName,nonneg int index,nonneg int numFunction)1725 void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName,
1726                                                           nonneg int index, nonneg int numFunction)
1727 {
1728     if (!mSettings->severity.isEnabled(Severity::warning))
1729         return;
1730     std::ostringstream errmsg;
1731     errmsg << functionName << ": ";
1732     if (index == 0) {
1733         errmsg << "parameter positions start at 1, not 0";
1734     } else {
1735         errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given";
1736     }
1737     reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal);
1738 }
1739 
invalidScanfArgTypeError_s(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo)1740 void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1741 {
1742     const Severity::SeverityType severity = getSeverity(argInfo);
1743     if (!mSettings->severity.isEnabled(severity))
1744         return;
1745     std::ostringstream errmsg;
1746     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'";
1747     if (specifier[0] == 's')
1748         errmsg << "char";
1749     else if (specifier[0] == 'S')
1750         errmsg << "wchar_t";
1751     errmsg << " *\' but the argument type is ";
1752     argumentType(errmsg, argInfo);
1753     errmsg << ".";
1754     reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1755 }
invalidScanfArgTypeError_int(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo,bool isUnsigned)1756 void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned)
1757 {
1758     const Severity::SeverityType severity = getSeverity(argInfo);
1759     if (!mSettings->severity.isEnabled(severity))
1760         return;
1761     std::ostringstream errmsg;
1762     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1763     if (specifier[0] == 'h') {
1764         if (specifier[1] == 'h')
1765             errmsg << (isUnsigned ? "unsigned " : "") << "char";
1766         else
1767             errmsg << (isUnsigned ? "unsigned " : "") << "short";
1768     } else if (specifier[0] == 'l') {
1769         if (specifier[1] == 'l')
1770             errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1771         else
1772             errmsg << (isUnsigned ? "unsigned " : "") << "long";
1773     } else if (specifier.find("I32") != std::string::npos) {
1774         errmsg << (isUnsigned ? "unsigned " : "") << "__int32";
1775     } else if (specifier.find("I64") != std::string::npos) {
1776         errmsg << (isUnsigned ? "unsigned " : "") << "__int64";
1777     } else if (specifier[0] == 'I') {
1778         errmsg << (isUnsigned ? "size_t" : "ptrdiff_t");
1779     } else if (specifier[0] == 'j') {
1780         if (isUnsigned)
1781             errmsg << "uintmax_t";
1782         else
1783             errmsg << "intmax_t";
1784     } else if (specifier[0] == 'z') {
1785         if (specifier[1] == 'd' || specifier[1] == 'i')
1786             errmsg << "ssize_t";
1787         else
1788             errmsg << "size_t";
1789     } else if (specifier[0] == 't') {
1790         errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1791     } else if (specifier[0] == 'L') {
1792         errmsg << (isUnsigned ? "unsigned " : "") << "long long";
1793     } else {
1794         errmsg << (isUnsigned ? "unsigned " : "") << "int";
1795     }
1796     errmsg << " *\' but the argument type is ";
1797     argumentType(errmsg, argInfo);
1798     errmsg << ".";
1799     reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal);
1800 }
invalidScanfArgTypeError_float(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo)1801 void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1802 {
1803     const Severity::SeverityType severity = getSeverity(argInfo);
1804     if (!mSettings->severity.isEnabled(severity))
1805         return;
1806     std::ostringstream errmsg;
1807     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1808     if (specifier[0] == 'l' && specifier[1] != 'l')
1809         errmsg << "double";
1810     else if (specifier[0] == 'L')
1811         errmsg << "long double";
1812     else
1813         errmsg << "float";
1814     errmsg << " *\' but the argument type is ";
1815     argumentType(errmsg, argInfo);
1816     errmsg << ".";
1817     reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1818 }
1819 
invalidPrintfArgTypeError_s(const Token * tok,nonneg int numFormat,const ArgumentInfo * argInfo)1820 void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1821 {
1822     const Severity::SeverityType severity = getSeverity(argInfo);
1823     if (!mSettings->severity.isEnabled(severity))
1824         return;
1825     std::ostringstream errmsg;
1826     errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is ";
1827     argumentType(errmsg, argInfo);
1828     errmsg << ".";
1829     reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal);
1830 }
invalidPrintfArgTypeError_n(const Token * tok,nonneg int numFormat,const ArgumentInfo * argInfo)1831 void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1832 {
1833     const Severity::SeverityType severity = getSeverity(argInfo);
1834     if (!mSettings->severity.isEnabled(severity))
1835         return;
1836     std::ostringstream errmsg;
1837     errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is ";
1838     argumentType(errmsg, argInfo);
1839     errmsg << ".";
1840     reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal);
1841 }
invalidPrintfArgTypeError_p(const Token * tok,nonneg int numFormat,const ArgumentInfo * argInfo)1842 void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo)
1843 {
1844     const Severity::SeverityType severity = getSeverity(argInfo);
1845     if (!mSettings->severity.isEnabled(severity))
1846         return;
1847     std::ostringstream errmsg;
1848     errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is ";
1849     argumentType(errmsg, argInfo);
1850     errmsg << ".";
1851     reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, Certainty::normal);
1852 }
printfFormatType(std::ostream & os,const std::string & specifier,bool isUnsigned)1853 static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned)
1854 {
1855     os << "\'";
1856     if (specifier[0] == 'l') {
1857         if (specifier[1] == 'l')
1858             os << (isUnsigned ? "unsigned " : "") << "long long";
1859         else
1860             os << (isUnsigned ? "unsigned " : "") << "long";
1861     } else if (specifier[0] == 'h') {
1862         if (specifier[1] == 'h')
1863             os << (isUnsigned ? "unsigned " : "") << "char";
1864         else
1865             os << (isUnsigned ? "unsigned " : "") << "short";
1866     } else if (specifier.find("I32") != std::string::npos) {
1867         os << (isUnsigned ? "unsigned " : "") << "__int32";
1868     } else if (specifier.find("I64") != std::string::npos) {
1869         os << (isUnsigned ? "unsigned " : "") << "__int64";
1870     } else if (specifier[0] == 'I') {
1871         os << (isUnsigned ? "size_t" : "ptrdiff_t");
1872     } else if (specifier[0] == 'j') {
1873         if (isUnsigned)
1874             os << "uintmax_t";
1875         else
1876             os << "intmax_t";
1877     } else if (specifier[0] == 'z') {
1878         if (specifier[1] == 'd' || specifier[1] == 'i')
1879             os << "ssize_t";
1880         else
1881             os << "size_t";
1882     } else if (specifier[0] == 't') {
1883         os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t";
1884     } else if (specifier[0] == 'L') {
1885         os << (isUnsigned ? "unsigned " : "") << "long long";
1886     } else {
1887         os << (isUnsigned ? "unsigned " : "") << "int";
1888     }
1889     os << "\'";
1890 }
1891 
invalidPrintfArgTypeError_uint(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo)1892 void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1893 {
1894     const Severity::SeverityType severity = getSeverity(argInfo);
1895     if (!mSettings->severity.isEnabled(severity))
1896         return;
1897     std::ostringstream errmsg;
1898     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1899     printfFormatType(errmsg, specifier, true);
1900     errmsg << " but the argument type is ";
1901     argumentType(errmsg, argInfo);
1902     errmsg << ".";
1903     reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal);
1904 }
1905 
invalidPrintfArgTypeError_sint(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo)1906 void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1907 {
1908     const Severity::SeverityType severity = getSeverity(argInfo);
1909     if (!mSettings->severity.isEnabled(severity))
1910         return;
1911     std::ostringstream errmsg;
1912     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires ";
1913     printfFormatType(errmsg, specifier, false);
1914     errmsg << " but the argument type is ";
1915     argumentType(errmsg, argInfo);
1916     errmsg << ".";
1917     reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal);
1918 }
invalidPrintfArgTypeError_float(const Token * tok,nonneg int numFormat,const std::string & specifier,const ArgumentInfo * argInfo)1919 void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo)
1920 {
1921     const Severity::SeverityType severity = getSeverity(argInfo);
1922     if (!mSettings->severity.isEnabled(severity))
1923         return;
1924     std::ostringstream errmsg;
1925     errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'";
1926     if (specifier[0] == 'L')
1927         errmsg << "long ";
1928     errmsg << "double\' but the argument type is ";
1929     argumentType(errmsg, argInfo);
1930     errmsg << ".";
1931     reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal);
1932 }
1933 
getSeverity(const CheckIO::ArgumentInfo * argInfo)1934 Severity::SeverityType CheckIO::getSeverity(const CheckIO::ArgumentInfo *argInfo)
1935 {
1936     return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning;
1937 }
1938 
argumentType(std::ostream & os,const ArgumentInfo * argInfo)1939 void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
1940 {
1941     if (argInfo) {
1942         os << "\'";
1943         const Token *type = argInfo->typeToken;
1944         if (type->tokType() == Token::eString) {
1945             if (type->isLong())
1946                 os << "const wchar_t *";
1947             else
1948                 os << "const char *";
1949         } else {
1950             if (type->originalName().empty()) {
1951                 if (type->strAt(-1) == "const")
1952                     os << "const ";
1953                 while (Token::Match(type, "const|struct")) {
1954                     os << type->str() << " ";
1955                     type = type->next();
1956                 }
1957                 while (Token::Match(type, "%any% ::")) {
1958                     os << type->str() << "::";
1959                     type = type->tokAt(2);
1960                 }
1961                 os << type->stringify(false, true, false);
1962                 if (type->strAt(1) == "*" && !argInfo->element)
1963                     os << " *";
1964                 else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
1965                     os << " *";
1966                 else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray())
1967                     os << " *";
1968                 if (argInfo->address)
1969                     os << " *";
1970             } else {
1971                 if (type->isUnsigned()) {
1972                     if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t")
1973                         os << "unsigned ";
1974                 }
1975                 os << type->originalName();
1976                 if (type->strAt(1) == "*" || argInfo->address)
1977                     os << " *";
1978                 os << " {aka " << type->stringify(false, true, false);
1979                 if (type->strAt(1) == "*" || argInfo->address)
1980                     os << " *";
1981                 os << "}";
1982             }
1983         }
1984         os << "\'";
1985     } else
1986         os << "Unknown";
1987 }
1988 
invalidLengthModifierError(const Token * tok,nonneg int numFormat,const std::string & modifier)1989 void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier)
1990 {
1991     if (!mSettings->severity.isEnabled(Severity::warning))
1992         return;
1993     std::ostringstream errmsg;
1994     errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
1995     reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal);
1996 }
1997 
invalidScanfFormatWidthError(const Token * tok,nonneg int numFormat,int width,const Variable * var,char c)1998 void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, char c)
1999 {
2000     MathLib::bigint arrlen = 0;
2001     std::string varname;
2002 
2003     if (var) {
2004         arrlen = var->dimension(0);
2005         varname = var->name();
2006     }
2007 
2008     std::ostringstream errmsg;
2009     if (arrlen > width) {
2010         if (tok != nullptr && (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)))
2011             return;
2012         errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer"
2013                << " '" << varname << "[" << arrlen << "]'.";
2014         reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), Certainty::inconclusive);
2015     } else {
2016         errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '"
2017                << varname << "[" << arrlen << "]', use %" << (c == 'c' ? arrlen : (arrlen - 1)) << c << " to prevent overflowing it.";
2018         reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal);
2019     }
2020 }
2021