Branch data Line data Source code
1 : : /*
2 : : * Cppcheck - A tool for static C/C++ code analysis
3 : : * Copyright (C) 2007-2013 Daniel Marjamäki and 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 "tokenize.h"
23 : : #include "token.h"
24 : : #include "errorlogger.h"
25 : : #include "symboldatabase.h"
26 : :
27 : : #include <cctype>
28 : : #include <cstdlib>
29 : :
30 : : //---------------------------------------------------------------------------
31 : :
32 : : // Register CheckIO..
33 : : namespace {
34 : 45 : CheckIO instance;
35 : : }
36 : :
37 : :
38 : : //---------------------------------------------------------------------------
39 : : // std::cout << std::cout;
40 : : //---------------------------------------------------------------------------
41 : 3796 : void CheckIO::checkCoutCerrMisusage()
42 : : {
43 : 3796 : const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
44 : 3796 : std::size_t functions = symbolDatabase->functionScopes.size();
45 [ + + ]: 7682 : for (std::size_t i = 0; i < functions; ++i) {
46 : 3886 : const Scope * scope = symbolDatabase->functionScopes[i];
47 : 3886 : bool firstCout = false;
48 [ + - ][ + + ]: 72728 : for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
[ + + ]
49 [ + + ]: 68842 : if (tok->str() == "(")
50 : 12411 : tok = tok->link();
51 : :
52 [ + + ]: 68842 : if (Token::Match(tok, "std :: cout|cerr")) {
53 [ + + ][ + - ]: 450 : if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") {
[ + + ][ + + ]
54 : 135 : coutCerrMisusageError(tok, tok->strAt(2));
55 : 135 : firstCout = false;
56 [ + + ]: 315 : } else if (tok->strAt(3) == "<<")
57 : 270 : firstCout = true;
58 [ + + ][ + + ]: 68392 : } else if (firstCout && tok->str() == ";")
[ + + ]
59 : 135 : firstCout = false;
60 : : }
61 : : }
62 : 3796 : }
63 : :
64 : 180 : void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName)
65 : : {
66 [ + - ][ + - ]: 180 : reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.");
[ + - ][ + - ]
[ + - ]
67 : 180 : }
68 : :
69 : : //---------------------------------------------------------------------------
70 : : // fflush(stdin) <- fflush only applies to output streams in ANSI C
71 : : // fread(); fwrite(); <- consecutive read/write statements require repositioning in between
72 : : // fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
73 : : // fclose(); fread(); <- Use closed file
74 : : //---------------------------------------------------------------------------
75 : : enum OpenMode {CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN};
76 : 990 : static OpenMode getMode(const std::string& str)
77 : : {
78 [ + + ]: 990 : if (str.find('+', 1) != std::string::npos)
79 : 135 : return RW_MODE;
80 [ + + ][ + + ]: 855 : else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos)
[ + + ]
81 : 360 : return WRITE_MODE;
82 [ + + ]: 495 : else if (str.find('r') != std::string::npos)
83 : 360 : return READ_MODE;
84 : 990 : return UNKNOWN;
85 : : }
86 : :
87 : : struct Filepointer {
88 : : OpenMode mode;
89 : : unsigned int mode_indent;
90 : : enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation;
91 : : unsigned int op_indent;
92 : 1755 : Filepointer(OpenMode mode_ = UNKNOWN)
93 : 1755 : : mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0) {
94 : 1755 : }
95 : : };
96 : :
97 : 3796 : void CheckIO::checkFileUsage()
98 : : {
99 : : static const char* _whitelist[] = {
100 : : "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc"
101 : : };
102 [ + + ][ + - ]: 3796 : static const std::set<std::string> whitelist(_whitelist, _whitelist + sizeof(_whitelist)/sizeof(*_whitelist));
[ + - ][ # # ]
103 : :
104 : 3796 : std::map<unsigned int, Filepointer> filepointers;
105 : :
106 : 3796 : const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
107 [ + - ]: 3796 : std::size_t varListSize = symbolDatabase->getVariableListSize();
108 [ + + ]: 10987 : for (std::size_t i = 1; i < varListSize; ++i) {
109 [ + - ]: 7191 : const Variable* var = symbolDatabase->getVariableFromVarId(i);
110 [ + + ][ + - ]: 7191 : if (!var || !var->varId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *"))
[ + + ][ + - ]
[ + + ][ + + ]
111 : 5571 : continue;
112 : :
113 [ + - ][ + + ]: 1620 : if (var->isLocal()) {
114 [ + - ][ + - ]: 270 : if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor"
[ + + ]
115 [ + - ][ + - ]: 45 : filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN)));
116 : : else
117 [ + - ][ + - ]: 225 : filepointers.insert(std::make_pair(var->varId(), Filepointer(CLOSED)));
118 : : } else {
119 [ + - ][ + - ]: 1350 : filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN)));
120 : : // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
121 : : }
122 : : }
123 : :
124 : 3796 : std::size_t functions = symbolDatabase->functionScopes.size();
125 [ + + ]: 7682 : for (std::size_t j = 0; j < functions; ++j) {
126 : 3886 : const Scope * scope = symbolDatabase->functionScopes[j];
127 : 3886 : unsigned int indent = 0;
128 [ + + ]: 131750 : for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
129 [ + - ][ + + ]: 127864 : if (tok->str() == "{")
130 : 6217 : indent++;
131 [ + - ][ + + ]: 121647 : else if (tok->str() == "}") {
132 : 2331 : indent--;
133 [ + - ][ + - ]: 3096 : for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
[ + + ]
134 [ + - ][ + + ]: 765 : if (indent < i->second.mode_indent) {
135 [ + - ]: 180 : i->second.mode_indent = 0;
136 [ + - ]: 180 : i->second.mode = UNKNOWN;
137 : : }
138 [ + - ][ + + ]: 765 : if (indent < i->second.op_indent) {
139 [ + - ]: 540 : i->second.op_indent = 0;
140 [ + - ]: 540 : i->second.lastOperation = Filepointer::UNKNOWN_OP;
141 : : }
142 : : }
143 [ + - ][ + + ]: 119316 : } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break") { // Reset upon return, continue or break
[ + - ][ + - ]
[ + - ][ - + ]
[ + + ]
144 [ + - ][ + - ]: 480 : for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
[ + + ]
145 [ + - ]: 225 : i->second.mode_indent = 0;
146 [ + - ]: 225 : i->second.mode = UNKNOWN;
147 [ + - ]: 225 : i->second.op_indent = 0;
148 [ + - ]: 225 : i->second.lastOperation = Filepointer::UNKNOWN_OP;
149 : : }
150 [ + + ][ + - ]: 119061 : } else if (tok->varId() && Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile")) {
[ + + ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + + ]
151 [ + - ]: 1791 : std::map<unsigned int, Filepointer>::iterator i = filepointers.find(tok->varId());
152 [ + - ][ + + ]: 1791 : if (i != filepointers.end()) {
153 [ + - ]: 45 : i->second.mode = UNKNOWN;
154 [ + - ]: 45 : i->second.lastOperation = Filepointer::UNKNOWN_OP;
155 : : }
156 [ + - ][ + + ]: 117270 : } else if (Token::Match(tok, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
[ + - ][ + + ]
[ + - ][ - + ]
[ + + ]
157 [ + - ]: 12906 : std::string mode;
158 : 12906 : const Token* fileTok = 0;
159 : 12906 : Filepointer::Operation operation = Filepointer::NONE;
160 : :
161 [ + - ][ + + ]: 12906 : if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile") && tok->strAt(-1) == "=") {
[ + - ][ + + ]
[ + - ][ - + ]
[ + - ][ + - ]
[ + - ][ + + ]
162 [ + - ][ + - ]: 1035 : if (tok->str() != "tmpfile") {
163 [ + - ][ + - ]: 1035 : const Token* modeTok = tok->tokAt(2)->nextArgument();
164 [ + - ][ + + ]: 1035 : if (modeTok && modeTok->type() == Token::eString)
[ + + ]
165 [ + - ][ + - ]: 855 : mode = modeTok->strValue();
[ + - ]
166 : : } else
167 [ # # ]: 0 : mode = "wb+";
168 [ + - ]: 1035 : fileTok = tok->tokAt(-2);
169 : 1035 : operation = Filepointer::OPEN;
170 [ + - ][ + + ]: 11871 : } else if (tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") {
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + + ]
171 [ + - ][ + + ]: 405 : if (Token::simpleMatch(tok, "fflush ( stdin )"))
172 [ + - ][ + - ]: 45 : fflushOnInputStreamError(tok, tok->strAt(2));
173 : : else {
174 [ + - ]: 360 : fileTok = tok->tokAt(2);
175 : 360 : operation = Filepointer::POSITIONING;
176 : : }
177 [ + - ][ + - ]: 11466 : } else if (tok->str() == "fgetc" || tok->str() == "fgets" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "getc") {
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ - + ]
[ + + ]
178 [ + - ][ + + ]: 1035 : if (tok->str() == "fscanf")
179 [ + - ]: 135 : fileTok = tok->tokAt(2);
180 : : else
181 [ + - ]: 900 : fileTok = tok->linkAt(1)->previous();
182 : 1035 : operation = Filepointer::READ;
183 [ + - ][ + - ]: 10431 : } else if (tok->str() == "fputc" || tok->str() == "fputs" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "putcc") {
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ - + ]
[ + + ]
184 [ + - ][ + + ]: 1395 : if (tok->str() == "fprintf")
185 [ + - ]: 270 : fileTok = tok->tokAt(2);
186 : : else
187 [ + - ]: 1125 : fileTok = tok->linkAt(1)->previous();
188 : 1395 : operation = Filepointer::WRITE;
189 [ + - ][ + + ]: 9036 : } else if (tok->str() == "fclose") {
190 [ + - ]: 585 : fileTok = tok->tokAt(2);
191 : 585 : operation = Filepointer::CLOSE;
192 [ + - ][ + - ]: 8451 : } else if (whitelist.find(tok->str()) != whitelist.end()) {
[ + + ]
193 [ + - ]: 225 : fileTok = tok->tokAt(2);
194 [ + - ][ + + ]: 225 : if (tok->str() == "ungetc" && fileTok)
[ + - ][ + + ]
195 [ + - ]: 45 : fileTok = fileTok->nextArgument();
196 : 225 : operation = Filepointer::UNIMPORTANT;
197 [ + - ][ + + ]: 8226 : } else if (!Token::Match(tok, "if|for|while|catch|switch")) {
198 [ + - ]: 7821 : const Token* const end2 = tok->linkAt(1);
199 [ + - ][ + + ]: 32472 : for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
200 [ + + ][ + - ]: 24651 : if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) {
[ + - ][ + + ]
[ + + ]
201 : 45 : fileTok = tok2;
202 : 45 : operation = Filepointer::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now
203 : 45 : break;
204 : : }
205 : : }
206 : : }
207 : :
208 [ + - ][ + + ]: 12996 : while (Token::Match(fileTok, "%var% ."))
209 [ + - ]: 90 : fileTok = fileTok->tokAt(2);
210 : :
211 [ + + ][ + + ]: 12906 : if (!fileTok || !fileTok->varId())
[ + + ]
212 : 8586 : continue;
213 : :
214 [ + - ][ + - ]: 4320 : if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File
[ + + ]
215 [ + - ][ + - ]: 135 : filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN)));
216 : : }
217 [ + - ]: 4320 : Filepointer& f = filepointers[fileTok->varId()];
218 : :
219 [ + + + + : 4320 : switch (operation) {
+ + + - ]
220 : : case Filepointer::OPEN:
221 [ + - ]: 990 : f.mode = getMode(mode);
222 : 990 : f.mode_indent = indent;
223 : 990 : break;
224 : : case Filepointer::POSITIONING:
225 [ - + ]: 315 : if (f.mode == CLOSED)
226 [ # # ]: 0 : useClosedFileError(tok);
227 : 315 : break;
228 : : case Filepointer::READ:
229 [ + + ]: 945 : if (f.mode == CLOSED)
230 [ + - ]: 45 : useClosedFileError(tok);
231 [ + + ]: 900 : else if (f.mode == WRITE_MODE)
232 [ + - ]: 180 : readWriteOnlyFileError(tok);
233 [ + + ]: 720 : else if (f.lastOperation == Filepointer::WRITE)
234 [ + - ]: 45 : ioWithoutPositioningError(tok);
235 : 945 : break;
236 : : case Filepointer::WRITE:
237 [ + + ]: 1215 : if (f.mode == CLOSED)
238 [ + - ]: 135 : useClosedFileError(tok);
239 [ + + ]: 1080 : else if (f.mode == READ_MODE)
240 [ + - ]: 135 : writeReadOnlyFileError(tok);
241 [ + + ]: 945 : else if (f.lastOperation == Filepointer::READ)
242 [ + - ]: 90 : ioWithoutPositioningError(tok);
243 : 1215 : break;
244 : : case Filepointer::CLOSE:
245 [ - + ]: 585 : if (f.mode == CLOSED)
246 [ # # ]: 0 : useClosedFileError(tok);
247 : : else
248 : 585 : f.mode = CLOSED;
249 : 585 : f.mode_indent = indent;
250 : 585 : break;
251 : : case Filepointer::UNIMPORTANT:
252 [ + + ]: 225 : if (f.mode == CLOSED)
253 [ + - ]: 90 : useClosedFileError(tok);
254 : 225 : break;
255 : : case Filepointer::UNKNOWN_OP:
256 : 45 : f.mode = UNKNOWN;
257 : 45 : f.mode_indent = 0;
258 : 45 : break;
259 : : default:
260 : 0 : break;
261 : : }
262 [ + - ][ + + ]: 4320 : if (operation != Filepointer::NONE && operation != Filepointer::UNIMPORTANT) {
263 : 4095 : f.op_indent = indent;
264 : 4320 : f.lastOperation = operation;
265 [ + - ]: 12906 : }
266 : : }
267 : : }
268 [ + - ][ + - ]: 5686 : for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
[ + + ]
269 [ + - ]: 1800 : i->second.op_indent = 0;
270 [ + - ]: 1800 : i->second.mode = UNKNOWN;
271 [ + - ]: 1800 : i->second.lastOperation = Filepointer::UNKNOWN_OP;
272 : : }
273 : 3796 : }
274 : 3796 : }
275 : :
276 : 90 : void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname)
277 : : {
278 : : reportError(tok, Severity::error,
279 [ + - ][ + - ]: 90 : "fflushOnInputStream", "fflush() called on input stream '" + varname + "' results in undefined behaviour.");
[ + - ][ + - ]
[ + - ]
280 : 90 : }
281 : :
282 : 180 : void CheckIO::ioWithoutPositioningError(const Token *tok)
283 : : {
284 : : reportError(tok, Severity::error,
285 [ + - ][ + - ]: 180 : "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.");
[ + - ][ + - ]
[ + - ]
286 : 180 : }
287 : :
288 : 225 : void CheckIO::readWriteOnlyFileError(const Token *tok)
289 : : {
290 : :
291 : : reportError(tok, Severity::error,
292 [ + - ][ + - ]: 225 : "readWriteOnlyFile", "Read operation on a file that was opened only for writing.");
[ + - ][ + - ]
[ + - ]
293 : 225 : }
294 : :
295 : 180 : void CheckIO::writeReadOnlyFileError(const Token *tok)
296 : : {
297 : : reportError(tok, Severity::error,
298 [ + - ][ + - ]: 180 : "writeReadOnlyFile", "Write operation on a file that was opened only for reading.");
[ + - ][ + - ]
[ + - ]
299 : 180 : }
300 : :
301 : 315 : void CheckIO::useClosedFileError(const Token *tok)
302 : : {
303 : : reportError(tok, Severity::error,
304 [ + - ][ + - ]: 315 : "useClosedFile", "Used file that is not opened.");
[ + - ][ + - ]
[ + - ]
305 : 315 : }
306 : :
307 : :
308 : : //---------------------------------------------------------------------------
309 : : // scanf without field width limits can crash with huge input data
310 : : //---------------------------------------------------------------------------
311 : 3796 : void CheckIO::invalidScanf()
312 : : {
313 [ + - ][ + - ]: 3796 : if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability"))
[ + + ][ + - ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + + ][ # # ]
[ # # ][ # # ]
[ # # ]
314 : 3796 : return;
315 : :
316 : 3060 : const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase();
317 : 3060 : std::size_t functions = symbolDatabase->functionScopes.size();
318 [ + + ]: 6210 : for (std::size_t j = 0; j < functions; ++j) {
319 : 3150 : const Scope * scope = symbolDatabase->functionScopes[j];
320 [ + + ]: 103905 : for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
321 : 100755 : const Token *formatToken = 0;
322 [ + + ]: 100755 : if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
323 : 945 : formatToken = tok->tokAt(2);
324 [ + + ]: 99810 : else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
325 : 720 : const Token* nextArg = tok->tokAt(2)->nextArgument();
326 [ + - ][ + - ]: 720 : if (nextArg && nextArg->type() == Token::eString)
[ + - ]
327 : 720 : formatToken = nextArg;
328 : : else
329 : 0 : continue;
330 : : } else
331 : 99090 : continue;
332 : :
333 : 1665 : bool format = false;
334 : :
335 : : // scan the string backwards, so we dont need to keep states
336 : 1665 : const std::string &formatstr(formatToken->str());
337 [ + + ]: 11700 : for (unsigned int i = 1; i < formatstr.length(); i++) {
338 [ + + ]: 10035 : if (formatstr[i] == '%')
339 : 2160 : format = !format;
340 : :
341 [ + + ]: 7875 : else if (!format)
342 : 5805 : continue;
343 : :
344 [ + + ][ + + ]: 2070 : else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') {
[ + + ]
345 : 1485 : format = false;
346 : : }
347 : :
348 [ + + ][ + - ]: 585 : else if (std::isalpha(formatstr[i]) || formatstr[i] == '[') {
[ + - ]
349 [ + - ][ + + ]: 585 : if ((formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) && _settings->isEnabled("warning")) // #3490 - field width limits are only necessary for string input
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + + ]
[ + + ][ # # ]
[ # # ]
350 : 270 : invalidScanfError(tok, false);
351 [ + - ][ + + ]: 315 : else if (formatstr[i] != 'n' && formatstr[i] != 'c' && _settings->platformType != Settings::Win32A && _settings->platformType != Settings::Win32W && _settings->platformType != Settings::Win64 && _settings->isEnabled("portability"))
[ + - ][ + + ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ + - ]
[ + + ][ + + ]
[ # # ][ # # ]
352 : 45 : invalidScanfError(tok, true); // Warn about libc bug in versions prior to 2.13-25
353 : 585 : format = false;
354 : : }
355 : : }
356 : : }
357 : : }
358 : : }
359 : :
360 : 360 : void CheckIO::invalidScanfError(const Token *tok, bool portability)
361 : : {
362 [ + + ]: 360 : if (portability)
363 : : reportError(tok, Severity::portability,
364 : : "invalidscanf", "scanf without field width limits can crash with huge input data on some versions of libc.\n"
365 : : "scanf without field width limits can crash with huge input data on libc versions older than 2.13-25. Add a field "
366 : : "width specifier to fix this problem:\n"
367 : : " %i => %3i\n"
368 : : "\n"
369 : : "Sample program that can crash:\n"
370 : : "\n"
371 : : "#include <stdio.h>\n"
372 : : "int main()\n"
373 : : "{\n"
374 : : " int a;\n"
375 : : " scanf(\"%i\", &a);\n"
376 : : " return 0;\n"
377 : : "}\n"
378 : : "\n"
379 : : "To make it crash:\n"
380 [ + - ][ + - ]: 45 : "perl -e 'print \"5\"x2100000' | ./a.out");
[ + - ][ + - ]
[ + - ]
381 : : else
382 : : reportError(tok, Severity::warning,
383 : : "invalidscanf", "scanf without field width limits can crash with huge input data.\n"
384 : : "scanf without field width limits can crash with huge input data. Add a field width "
385 : : "specifier to fix this problem:\n"
386 : : " %s => %20s\n"
387 : : "\n"
388 : : "Sample program that can crash:\n"
389 : : "\n"
390 : : "#include <stdio.h>\n"
391 : : "int main()\n"
392 : : "{\n"
393 : : " char c[5];\n"
394 : : " scanf(\"%s\", c);\n"
395 : : " return 0;\n"
396 : : "}\n"
397 : : "\n"
398 [ + - ][ + - ]: 315 : "To make it crash, type in more than 5 characters.");
[ + - ][ + - ]
[ + - ]
399 : 360 : }
400 : :
401 : : //---------------------------------------------------------------------------
402 : : // printf("%u", "xyz"); // Wrong argument type
403 : : // printf("%u%s", 1); // Too few arguments
404 : : // printf("", 1); // Too much arguments
405 : : //---------------------------------------------------------------------------
406 : 900 : static bool isComplexType(const Variable* var, const Token* varTypeTok)
407 : : {
408 [ + + ]: 900 : if (var->type())
409 : 360 : return(true);
410 : :
411 [ + + ][ + - ]: 540 : static std::set<std::string> knownTypes;
[ + - ][ # # ]
412 [ + + ]: 540 : if (knownTypes.empty()) {
413 [ + - ][ + - ]: 45 : knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type
[ + - ]
414 [ + - ][ + - ]: 45 : knownTypes.insert("string");
[ + - ]
415 [ + - ][ + - ]: 45 : knownTypes.insert("wstring");
[ + - ]
416 : : }
417 : :
418 [ + + ]: 540 : if (varTypeTok->str() == "std")
419 : 90 : varTypeTok = varTypeTok->tokAt(2);
420 [ + + ][ - + ]: 900 : return((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !var->isPointer() && !var->isArray());
[ # # ][ # # ]
[ + - ][ + - ]
421 : : }
422 : :
423 : 2475 : static bool isKnownType(const Variable* var, const Token* varTypeTok)
424 : : {
425 [ + + ][ + - ]: 2475 : return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok));
[ + + ]
426 : : }
427 : :
428 : 3796 : void CheckIO::checkWrongPrintfScanfArguments()
429 : : {
430 : 3796 : const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
431 [ + - ][ + - ]: 3796 : bool warning = _settings->isEnabled("warning");
[ + - ]
432 : :
433 : 3796 : std::size_t functions = symbolDatabase->functionScopes.size();
434 [ + + ]: 7682 : for (std::size_t j = 0; j < functions; ++j) {
435 : 3886 : const Scope * scope = symbolDatabase->functionScopes[j];
436 [ + + ]: 128689 : for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
437 [ + + ]: 124803 : if (!tok->isName()) continue;
438 : :
439 : 38469 : const Token* argListTok = 0; // Points to first va_list argument
440 : 38469 : std::string formatString;
441 : :
442 [ + - ][ + + ]: 38469 : if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%")) {
443 [ + - ][ + - ]: 4725 : formatString = tok->strAt(2);
444 [ + - ][ + - ]: 4725 : if (tok->strAt(3) == ",") {
[ + + ]
445 [ + - ]: 4545 : argListTok = tok->tokAt(4);
446 [ + - ][ + - ]: 180 : } else if (tok->strAt(3) == ")") {
[ + + ]
447 : 135 : argListTok = 0;
448 : : } else {
449 : 45 : continue;
450 : : }
451 [ + - ][ + + ]: 33744 : } else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%")) {
452 [ + - ][ + - ]: 1080 : const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string)
453 [ + - ][ + + ]: 1080 : if (Token::Match(formatStringTok, "%str% ,")) {
454 [ + - ]: 855 : argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args)
455 [ + - ]: 855 : formatString = formatStringTok->str();
456 [ + - ][ + + ]: 225 : } else if (Token::Match(formatStringTok, "%str% )")) {
457 : 180 : argListTok = 0; // Find third parameter (first argument of va_args)
458 [ + - ]: 180 : formatString = formatStringTok->str();
459 : : } else {
460 : 45 : continue;
461 : : }
462 [ + - ][ + + ]: 32664 : } else if (Token::Match(tok, "snprintf|fnprintf|swprintf (")) {
463 [ + - ]: 135 : const Token* formatStringTok = tok->tokAt(2);
464 [ + + ][ + - ]: 405 : for (int i = 0; i < 2 && formatStringTok; i++) {
[ + + ]
465 [ + - ]: 270 : formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string)
466 : : }
467 [ + - ][ + + ]: 135 : if (Token::Match(formatStringTok, "%str% ,")) {
468 [ + - ]: 45 : argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
469 [ + - ]: 45 : formatString = formatStringTok->str();
470 [ + - ][ + + ]: 90 : } else if (Token::Match(formatStringTok, "%str% )")) {
471 : 45 : argListTok = 0; // Find fourth parameter (first argument of va_args)
472 [ + - ]: 45 : formatString = formatStringTok->str();
473 : : } else {
474 : 45 : continue;
475 : : }
476 : : } else {
477 : 32529 : continue;
478 : : }
479 : :
480 : : // Count format string parameters..
481 [ + - ]: 5805 : bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf");
482 : 5805 : unsigned int numFormat = 0;
483 : 5805 : bool percent = false;
484 : 5805 : const Token* argListTok2 = argListTok;
485 [ + - ][ + - ]: 38520 : for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) {
[ + - ][ + + ]
486 [ + + ]: 32760 : if (*i == '%') {
487 : 7650 : percent = !percent;
488 [ + + ][ + + ]: 25110 : } else if (percent && *i == '[') {
[ + + ]
489 [ + - ][ + - ]: 180 : while (i != formatString.end()) {
[ + - ]
490 [ + + ]: 180 : if (*i == ']') {
491 : 45 : numFormat++;
492 [ + - ]: 45 : if (argListTok)
493 [ + - ]: 45 : argListTok = argListTok->nextArgument();
494 : 45 : percent = false;
495 : 45 : break;
496 : : }
497 : 135 : ++i;
498 : : }
499 [ + - ][ + - ]: 45 : if (i == formatString.end())
[ - + ]
500 : 0 : break;
501 [ + + ]: 25065 : } else if (percent) {
502 : 7155 : percent = false;
503 : :
504 : 7155 : bool _continue = false;
505 [ + - ]: 7155 : std::string width;
506 [ + - ][ + - ]: 9900 : while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
[ + + ][ + + ]
[ + + ][ + + ]
507 [ + + ]: 2745 : if (*i == '*') {
508 [ + + ]: 450 : if (scan)
509 : 225 : _continue = true;
510 : : else {
511 : 225 : numFormat++;
512 [ + - ]: 225 : if (argListTok)
513 [ + - ]: 225 : argListTok = argListTok->nextArgument();
514 : : }
515 [ + + ]: 2295 : } else if (std::isdigit(*i))
516 [ + - ]: 1710 : width += *i;
517 : 2745 : ++i;
518 : : }
519 [ + - ][ + - ]: 7155 : if (i == formatString.end())
[ + + ]
520 : : break;
521 [ + + ]: 7110 : if (_continue)
522 : 225 : continue;
523 : :
524 [ + + ][ + + ]: 6885 : if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
[ + + ]
525 : 6840 : numFormat++;
526 : :
527 : : // Perform type checks
528 [ + + ][ + - ]: 6840 : if (argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern.
[ + + ][ + + ]
529 : 4860 : const Variable *variableInfo = argListTok->variable();
530 [ + + ]: 4860 : const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
531 [ + + ][ + - ]: 4860 : if (varTypeTok && varTypeTok->str() == "static")
[ - + ][ - + ]
532 : 0 : varTypeTok = varTypeTok->next();
533 : :
534 [ + + ][ + + ]: 4860 : if (scan && varTypeTok) {
535 [ + - ][ + - ]: 405 : if (warning && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const"))
[ + - ][ + - ]
[ + - ][ - + ]
[ - + ]
536 [ # # ]: 0 : invalidScanfArgTypeError(tok, tok->str(), numFormat);
537 : :
538 [ + - ][ + - ]: 405 : if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1) && variableInfo->dimensions()[0].known) {
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ + + ]
539 [ + - ][ + + ]: 360 : if (!width.empty()) {
540 [ + - ]: 315 : int numWidth = std::atoi(width.c_str());
541 [ + - ][ + + ]: 315 : if (numWidth != (variableInfo->dimension(0) - 1))
542 [ + - ]: 225 : invalidScanfFormatWidthError(tok, numFormat, numWidth, variableInfo);
543 : : }
544 : 405 : }
545 [ + + ][ + - ]: 4455 : } else if (!scan && warning) {
546 [ + + + + : 4050 : switch (*i) {
+ + + + +
- ]
547 : : case 's':
548 [ + + ][ + - ]: 360 : if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
[ + - ][ + - ]
[ + + ][ + - ]
[ + + ]
549 [ + - ]: 135 : invalidPrintfArgTypeError_s(tok, numFormat);
550 : 360 : break;
551 : : case 'n':
552 [ + + ][ + - ]: 270 : if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
[ + - ][ + + ]
[ - + ][ + - ]
[ + - ][ + + ]
[ + + ][ + + ]
553 [ + - ]: 225 : invalidPrintfArgTypeError_n(tok, numFormat);
554 : 270 : break;
555 : : case 'c':
556 : : case 'x':
557 : : case 'X':
558 : : case 'o':
559 [ + + ][ + - ]: 405 : if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
[ + + ][ + - ]
[ + + ][ + - ]
[ + - ][ + + ]
560 [ + - ]: 90 : invalidPrintfArgTypeError_int(tok, numFormat, *i);
561 [ + + ]: 315 : else if (argListTok->type() == Token::eString)
562 [ + - ]: 45 : invalidPrintfArgTypeError_int(tok, numFormat, *i);
563 : 405 : break;
564 : : case 'd':
565 : : case 'i':
566 [ + + ][ + - ]: 540 : if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
[ + + ][ + + ]
[ + - ][ + + ]
567 [ + + ][ + - ]: 225 : if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char")
[ + + ][ + - ]
[ + + ][ + + ]
568 [ + - ]: 135 : invalidPrintfArgTypeError_sint(tok, numFormat, *i);
569 [ + + ]: 315 : } else if (argListTok->type() == Token::eString)
570 [ + - ]: 45 : invalidPrintfArgTypeError_sint(tok, numFormat, *i);
571 : 540 : break;
572 : : case 'u':
573 [ + + ][ + - ]: 945 : if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
[ + + ][ + + ]
[ + - ][ + + ]
574 [ - + ][ # # ]: 225 : if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool")
[ # # ][ + - ]
[ + + ][ + + ]
575 [ + - ]: 180 : invalidPrintfArgTypeError_uint(tok, numFormat, *i);
576 [ + + ]: 720 : } else if (argListTok->type() == Token::eString)
577 [ + - ]: 45 : invalidPrintfArgTypeError_uint(tok, numFormat, *i);
578 : 945 : break;
579 : : case 'p':
580 [ + - ][ + - ]: 270 : if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
[ + + ][ + - ]
[ + + ][ + - ]
[ + - ][ + + ]
581 [ + - ]: 135 : invalidPrintfArgTypeError_p(tok, numFormat);
582 [ - + ]: 135 : else if (argListTok->type() == Token::eString)
583 [ # # ]: 0 : invalidPrintfArgTypeError_p(tok, numFormat);
584 : 270 : break;
585 : : case 'e':
586 : : case 'E':
587 : : case 'f':
588 : : case 'g':
589 : : case 'G':
590 [ + + ][ + - ]: 315 : if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray()))
[ + + ][ + - ]
[ + + ][ + + ]
[ - + ][ + + ]
591 [ + - ]: 180 : invalidPrintfArgTypeError_float(tok, numFormat, *i);
592 [ + + ]: 135 : else if (argListTok->type() == Token::eString)
593 [ + - ]: 45 : invalidPrintfArgTypeError_float(tok, numFormat, *i);
594 : 315 : break;
595 : : case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
596 : : case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
597 : : // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
598 [ + - ][ + - ]: 540 : if (i != formatString.end() && *(i+1) == *i) {
[ + - ][ + - ]
[ + + ][ + + ]
599 [ + - ][ + - ]: 270 : if (i+1 != formatString.end() && !isalpha(*(i+2))) {
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ]
600 [ + - ]: 90 : std::string modifier;
601 [ + - ]: 90 : modifier += *i;
602 [ + - ][ + - ]: 90 : modifier += *(i+1);
603 [ + - ][ + - ]: 90 : invalidLengthModifierError(tok, numFormat, modifier);
604 : : }
605 : : } else {
606 [ + - ][ + - ]: 270 : if (i != formatString.end() && !isalpha(*(i+1))) {
[ + - ][ + - ]
[ + + ][ + + ]
607 [ + - ]: 90 : std::string modifier;
608 [ + - ]: 90 : modifier += *i;
609 [ + - ][ + - ]: 90 : invalidLengthModifierError(tok, numFormat, modifier);
610 : : }
611 : : }
612 : 540 : break;
613 : : case 'j': // intmax_t or uintmax_t
614 : : case 'z': // size_t
615 : : case 't': // ptrdiff_t
616 : : case 'L': // long double
617 : : // Expect an alphabetical character after these specifiers
618 [ + - ][ + - ]: 405 : if (i != formatString.end() && !isalpha(*(i+1))) {
[ + - ][ + - ]
[ + + ][ + + ]
619 [ + - ]: 180 : std::string modifier;
620 [ + - ]: 180 : modifier += *i;
621 [ + - ][ + - ]: 180 : invalidLengthModifierError(tok, numFormat, modifier);
622 : : }
623 : 405 : break;
624 : : default:
625 : 4860 : break;
626 : : }
627 : : }
628 : : }
629 : :
630 [ + + ]: 6840 : if (argListTok)
631 [ + - ]: 6885 : argListTok = argListTok->nextArgument(); // Find next argument
632 [ + - ][ + + ]: 7155 : }
633 : : }
634 : : }
635 : :
636 : : // Count printf/scanf parameters..
637 : 5805 : unsigned int numFunction = 0;
638 [ + + ]: 12600 : while (argListTok2) {
639 : 6795 : numFunction++;
640 [ + - ]: 6795 : argListTok2 = argListTok2->nextArgument(); // Find next argument
641 : : }
642 : :
643 : : // Mismatching number of parameters => warning
644 [ + + ]: 5805 : if (numFormat != numFunction)
645 [ + - ]: 5805 : wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction);
646 : 38469 : }
647 : : }
648 : 3796 : }
649 : :
650 : 990 : void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
651 : : const std::string &functionName,
652 : : unsigned int numFormat,
653 : : unsigned int numFunction)
654 : : {
655 [ + + ]: 990 : Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning;
656 [ + + ][ + - ]: 990 : if (severity != Severity::error && !_settings->isEnabled("style"))
[ + - ][ - + ]
[ + + ][ + - ]
[ + + ][ - + ]
[ # # ][ # # ]
657 : 990 : return;
658 : :
659 : 990 : std::ostringstream errmsg;
660 [ + - ]: 990 : errmsg << functionName
661 [ + - ]: 990 : << " format string has "
662 [ + - ]: 990 : << numFormat
663 [ + - ]: 990 : << " parameters but "
664 [ + + ][ + - ]: 1980 : << (numFormat > numFunction ? "only " : "")
665 [ + - ]: 990 : << numFunction
666 [ + - ]: 990 : << " are given.";
667 : :
668 [ + - ][ + - ]: 990 : reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str());
[ + - ][ + - ]
[ + - ]
669 : : }
670 : :
671 : 45 : void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat)
672 : : {
673 : 45 : std::ostringstream errmsg;
674 [ + - ][ + - ]: 45 : errmsg << functionName << " argument no. " << numFormat << ": requires a non-const pointer or array as argument.";
[ + - ][ + - ]
675 [ + - ][ + - ]: 45 : reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str());
[ + - ][ + - ]
[ + - ]
676 : 45 : }
677 : 180 : void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
678 : : {
679 : 180 : std::ostringstream errmsg;
680 [ + - ][ + - ]: 180 : errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list.";
[ + - ]
681 [ + - ][ + - ]: 180 : reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
[ + - ][ + - ]
[ + - ]
682 : 180 : }
683 : 270 : void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat)
684 : : {
685 : 270 : std::ostringstream errmsg;
686 [ + - ][ + - ]: 270 : errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list.";
[ + - ]
687 [ + - ][ + - ]: 270 : reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str());
[ + - ][ + - ]
[ + - ]
688 : 270 : }
689 : 180 : void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat)
690 : : {
691 : 180 : std::ostringstream errmsg;
692 [ + - ][ + - ]: 180 : errmsg << "%p in format string (no. " << numFormat << ") requires an address given in the argument list.";
[ + - ]
693 [ + - ][ + - ]: 180 : reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str());
[ + - ][ + - ]
[ + - ]
694 : 180 : }
695 : 180 : void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c)
696 : : {
697 : 180 : std::ostringstream errmsg;
698 [ + - ][ + - ]: 180 : errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list.";
[ + - ][ + - ]
[ + - ]
699 [ + - ][ + - ]: 180 : reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str());
[ + - ][ + - ]
[ + - ]
700 : 180 : }
701 : 270 : void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c)
702 : : {
703 : 270 : std::ostringstream errmsg;
704 [ + - ][ + - ]: 270 : errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list.";
[ + - ][ + - ]
[ + - ]
705 [ + - ][ + - ]: 270 : reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str());
[ + - ][ + - ]
[ + - ]
706 : 270 : }
707 : 225 : void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c)
708 : : {
709 : 225 : std::ostringstream errmsg;
710 [ + - ][ + - ]: 225 : errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list.";
[ + - ][ + - ]
[ + - ]
711 [ + - ][ + - ]: 225 : reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str());
[ + - ][ + - ]
[ + - ]
712 : 225 : }
713 : 270 : void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c)
714 : : {
715 : 270 : std::ostringstream errmsg;
716 [ + - ][ + - ]: 270 : errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list.";
[ + - ][ + - ]
[ + - ]
717 [ + - ][ + - ]: 270 : reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
[ + - ][ + - ]
[ + - ]
718 : 270 : }
719 : 360 : void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier)
720 : : {
721 : 360 : std::ostringstream errmsg;
722 [ + - ][ + - ]: 360 : errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";
[ + - ][ + - ]
[ + - ]
723 [ + - ][ + - ]: 360 : reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str());
[ + - ][ + - ]
[ + - ]
724 : 360 : }
725 : :
726 : 270 : void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var)
727 : : {
728 : 270 : std::ostringstream errmsg;
729 : 270 : Severity::SeverityType severity = Severity::warning;
730 : 270 : bool inconclusive = false;
731 : :
732 [ + + ]: 270 : if (var) {
733 [ + - ][ + + ]: 225 : if (var->dimension(0) > width) {
734 [ + + ]: 90 : if (!_settings->inconclusive)
735 : 270 : return;
736 : 45 : inconclusive = true;
737 [ + - ][ + - ]: 45 : errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer"
[ + - ][ + - ]
[ + - ]
738 [ + - ][ + - ]: 90 : << " '" << var->name() << "[" << var->dimension(0) << "]'.";
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
739 : : } else {
740 [ + - ][ + - ]: 135 : errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '"
[ + - ][ + - ]
[ + - ]
741 [ + - ][ + - ]: 270 : << var->name() << "[" << var->dimension(0) << "]', use %" << (var->dimension(0) - 1) << "s to prevent overflowing it.";
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
742 : 135 : severity = Severity::error;
743 : : }
744 : :
745 : : } else
746 [ + - ][ + - ]: 45 : errmsg << "Width " << width << " given in format string (no. " << numFormat << ") doesn't match destination buffer.";
[ + - ][ + - ]
[ + - ]
747 : :
748 [ + + ][ + - ]: 225 : if (severity == Severity::error || _settings->isEnabled("style"))
[ + - ][ + + ]
[ + + ][ + - ]
[ + + ][ + + ]
[ # # ][ # # ]
749 [ + - ][ + - ]: 270 : reportError(tok, severity, "invalidScanfFormatWidth", errmsg.str(), inconclusive);
[ + - ][ + - ]
[ + - ]
750 [ + - ][ + - ]: 135 : }
|