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