|
Cppcheck
|
00001 /* 00002 * Cppcheck - A tool for static C/C++ code analysis 00003 * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team. 00004 * 00005 * This program is free software: you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation, either version 3 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 //--------------------------------------------------------------------------- 00020 #include "checkio.h" 00021 00022 #include "tokenize.h" 00023 #include "token.h" 00024 #include "errorlogger.h" 00025 #include "symboldatabase.h" 00026 00027 #include <cctype> 00028 #include <cstdlib> 00029 00030 //--------------------------------------------------------------------------- 00031 00032 // Register CheckIO.. 00033 namespace { 00034 CheckIO instance; 00035 } 00036 00037 00038 //--------------------------------------------------------------------------- 00039 // std::cout << std::cout; 00040 //--------------------------------------------------------------------------- 00041 void CheckIO::checkCoutCerrMisusage() 00042 { 00043 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); 00044 std::size_t functions = symbolDatabase->functionScopes.size(); 00045 for (std::size_t i = 0; i < functions; ++i) { 00046 const Scope * scope = symbolDatabase->functionScopes[i]; 00047 bool firstCout = false; 00048 for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { 00049 if (tok->str() == "(") 00050 tok = tok->link(); 00051 00052 if (Token::Match(tok, "std :: cout|cerr")) { 00053 if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") { 00054 coutCerrMisusageError(tok, tok->strAt(2)); 00055 firstCout = false; 00056 } else if (tok->strAt(3) == "<<") 00057 firstCout = true; 00058 } else if (firstCout && tok->str() == ";") 00059 firstCout = false; 00060 } 00061 } 00062 } 00063 00064 void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) 00065 { 00066 reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'."); 00067 } 00068 00069 //--------------------------------------------------------------------------- 00070 // fflush(stdin) <- fflush only applies to output streams in ANSI C 00071 // fread(); fwrite(); <- consecutive read/write statements require repositioning in between 00072 // fopen("","r"); fwrite(); <- write to read-only file (or vice versa) 00073 // fclose(); fread(); <- Use closed file 00074 //--------------------------------------------------------------------------- 00075 enum OpenMode {CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN}; 00076 static OpenMode getMode(const std::string& str) 00077 { 00078 if (str.find('+', 1) != std::string::npos) 00079 return RW_MODE; 00080 else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos) 00081 return WRITE_MODE; 00082 else if (str.find('r') != std::string::npos) 00083 return READ_MODE; 00084 return UNKNOWN; 00085 } 00086 00087 struct Filepointer { 00088 OpenMode mode; 00089 unsigned int mode_indent; 00090 enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation; 00091 unsigned int op_indent; 00092 Filepointer(OpenMode mode_ = UNKNOWN) 00093 : mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0) { 00094 } 00095 }; 00096 00097 void CheckIO::checkFileUsage() 00098 { 00099 static const char* _whitelist[] = { 00100 "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc" 00101 }; 00102 static const std::set<std::string> whitelist(_whitelist, _whitelist + sizeof(_whitelist)/sizeof(*_whitelist)); 00103 00104 std::map<unsigned int, Filepointer> filepointers; 00105 00106 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 00107 std::size_t varListSize = symbolDatabase->getVariableListSize(); 00108 for (std::size_t i = 1; i < varListSize; ++i) { 00109 const Variable* var = symbolDatabase->getVariableFromVarId(i); 00110 if (!var || !var->varId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *")) 00111 continue; 00112 00113 if (var->isLocal()) { 00114 if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor" 00115 filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN))); 00116 else 00117 filepointers.insert(std::make_pair(var->varId(), Filepointer(CLOSED))); 00118 } else { 00119 filepointers.insert(std::make_pair(var->varId(), Filepointer(UNKNOWN))); 00120 // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode 00121 } 00122 } 00123 00124 std::size_t functions = symbolDatabase->functionScopes.size(); 00125 for (std::size_t j = 0; j < functions; ++j) { 00126 const Scope * scope = symbolDatabase->functionScopes[j]; 00127 unsigned int indent = 0; 00128 for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 00129 if (tok->str() == "{") 00130 indent++; 00131 else if (tok->str() == "}") { 00132 indent--; 00133 for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { 00134 if (indent < i->second.mode_indent) { 00135 i->second.mode_indent = 0; 00136 i->second.mode = UNKNOWN; 00137 } 00138 if (indent < i->second.op_indent) { 00139 i->second.op_indent = 0; 00140 i->second.lastOperation = Filepointer::UNKNOWN_OP; 00141 } 00142 } 00143 } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break") { // Reset upon return, continue or break 00144 for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { 00145 i->second.mode_indent = 0; 00146 i->second.mode = UNKNOWN; 00147 i->second.op_indent = 0; 00148 i->second.lastOperation = Filepointer::UNKNOWN_OP; 00149 } 00150 } else if (tok->varId() && Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile")) { 00151 std::map<unsigned int, Filepointer>::iterator i = filepointers.find(tok->varId()); 00152 if (i != filepointers.end()) { 00153 i->second.mode = UNKNOWN; 00154 i->second.lastOperation = Filepointer::UNKNOWN_OP; 00155 } 00156 } else if (Token::Match(tok, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { 00157 std::string mode; 00158 const Token* fileTok = 0; 00159 Filepointer::Operation operation = Filepointer::NONE; 00160 00161 if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile") && tok->strAt(-1) == "=") { 00162 if (tok->str() != "tmpfile") { 00163 const Token* modeTok = tok->tokAt(2)->nextArgument(); 00164 if (modeTok && modeTok->type() == Token::eString) 00165 mode = modeTok->strValue(); 00166 } else 00167 mode = "wb+"; 00168 fileTok = tok->tokAt(-2); 00169 operation = Filepointer::OPEN; 00170 } else if (tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") { 00171 if (Token::simpleMatch(tok, "fflush ( stdin )")) 00172 fflushOnInputStreamError(tok, tok->strAt(2)); 00173 else { 00174 fileTok = tok->tokAt(2); 00175 operation = Filepointer::POSITIONING; 00176 } 00177 } else if (tok->str() == "fgetc" || tok->str() == "fgets" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "getc") { 00178 if (tok->str() == "fscanf") 00179 fileTok = tok->tokAt(2); 00180 else 00181 fileTok = tok->linkAt(1)->previous(); 00182 operation = Filepointer::READ; 00183 } else if (tok->str() == "fputc" || tok->str() == "fputs" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "putcc") { 00184 if (tok->str() == "fprintf") 00185 fileTok = tok->tokAt(2); 00186 else 00187 fileTok = tok->linkAt(1)->previous(); 00188 operation = Filepointer::WRITE; 00189 } else if (tok->str() == "fclose") { 00190 fileTok = tok->tokAt(2); 00191 operation = Filepointer::CLOSE; 00192 } else if (whitelist.find(tok->str()) != whitelist.end()) { 00193 fileTok = tok->tokAt(2); 00194 if (tok->str() == "ungetc" && fileTok) 00195 fileTok = fileTok->nextArgument(); 00196 operation = Filepointer::UNIMPORTANT; 00197 } else if (!Token::Match(tok, "if|for|while|catch|switch")) { 00198 const Token* const end2 = tok->linkAt(1); 00199 for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { 00200 if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) { 00201 fileTok = tok2; 00202 operation = Filepointer::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now 00203 break; 00204 } 00205 } 00206 } 00207 00208 while (Token::Match(fileTok, "%var% .")) 00209 fileTok = fileTok->tokAt(2); 00210 00211 if (!fileTok || !fileTok->varId()) 00212 continue; 00213 00214 if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File 00215 filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN))); 00216 } 00217 Filepointer& f = filepointers[fileTok->varId()]; 00218 00219 switch (operation) { 00220 case Filepointer::OPEN: 00221 f.mode = getMode(mode); 00222 f.mode_indent = indent; 00223 break; 00224 case Filepointer::POSITIONING: 00225 if (f.mode == CLOSED) 00226 useClosedFileError(tok); 00227 break; 00228 case Filepointer::READ: 00229 if (f.mode == CLOSED) 00230 useClosedFileError(tok); 00231 else if (f.mode == WRITE_MODE) 00232 readWriteOnlyFileError(tok); 00233 else if (f.lastOperation == Filepointer::WRITE) 00234 ioWithoutPositioningError(tok); 00235 break; 00236 case Filepointer::WRITE: 00237 if (f.mode == CLOSED) 00238 useClosedFileError(tok); 00239 else if (f.mode == READ_MODE) 00240 writeReadOnlyFileError(tok); 00241 else if (f.lastOperation == Filepointer::READ) 00242 ioWithoutPositioningError(tok); 00243 break; 00244 case Filepointer::CLOSE: 00245 if (f.mode == CLOSED) 00246 useClosedFileError(tok); 00247 else 00248 f.mode = CLOSED; 00249 f.mode_indent = indent; 00250 break; 00251 case Filepointer::UNIMPORTANT: 00252 if (f.mode == CLOSED) 00253 useClosedFileError(tok); 00254 break; 00255 case Filepointer::UNKNOWN_OP: 00256 f.mode = UNKNOWN; 00257 f.mode_indent = 0; 00258 break; 00259 default: 00260 break; 00261 } 00262 if (operation != Filepointer::NONE && operation != Filepointer::UNIMPORTANT) { 00263 f.op_indent = indent; 00264 f.lastOperation = operation; 00265 } 00266 } 00267 } 00268 for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { 00269 i->second.op_indent = 0; 00270 i->second.mode = UNKNOWN; 00271 i->second.lastOperation = Filepointer::UNKNOWN_OP; 00272 } 00273 } 00274 } 00275 00276 void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) 00277 { 00278 reportError(tok, Severity::error, 00279 "fflushOnInputStream", "fflush() called on input stream '" + varname + "' results in undefined behaviour."); 00280 } 00281 00282 void CheckIO::ioWithoutPositioningError(const Token *tok) 00283 { 00284 reportError(tok, Severity::error, 00285 "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour."); 00286 } 00287 00288 void CheckIO::readWriteOnlyFileError(const Token *tok) 00289 { 00290 00291 reportError(tok, Severity::error, 00292 "readWriteOnlyFile", "Read operation on a file that was opened only for writing."); 00293 } 00294 00295 void CheckIO::writeReadOnlyFileError(const Token *tok) 00296 { 00297 reportError(tok, Severity::error, 00298 "writeReadOnlyFile", "Write operation on a file that was opened only for reading."); 00299 } 00300 00301 void CheckIO::useClosedFileError(const Token *tok) 00302 { 00303 reportError(tok, Severity::error, 00304 "useClosedFile", "Used file that is not opened."); 00305 } 00306 00307 00308 //--------------------------------------------------------------------------- 00309 // scanf without field width limits can crash with huge input data 00310 //--------------------------------------------------------------------------- 00311 void CheckIO::invalidScanf() 00312 { 00313 if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability")) 00314 return; 00315 00316 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); 00317 std::size_t functions = symbolDatabase->functionScopes.size(); 00318 for (std::size_t j = 0; j < functions; ++j) { 00319 const Scope * scope = symbolDatabase->functionScopes[j]; 00320 for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00321 const Token *formatToken = 0; 00322 if (Token::Match(tok, "scanf|vscanf ( %str% ,")) 00323 formatToken = tok->tokAt(2); 00324 else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { 00325 const Token* nextArg = tok->tokAt(2)->nextArgument(); 00326 if (nextArg && nextArg->type() == Token::eString) 00327 formatToken = nextArg; 00328 else 00329 continue; 00330 } else 00331 continue; 00332 00333 bool format = false; 00334 00335 // scan the string backwards, so we dont need to keep states 00336 const std::string &formatstr(formatToken->str()); 00337 for (unsigned int i = 1; i < formatstr.length(); i++) { 00338 if (formatstr[i] == '%') 00339 format = !format; 00340 00341 else if (!format) 00342 continue; 00343 00344 else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') { 00345 format = false; 00346 } 00347 00348 else if (std::isalpha(formatstr[i]) || formatstr[i] == '[') { 00349 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 00350 invalidScanfError(tok, false); 00351 else if (formatstr[i] != 'n' && formatstr[i] != 'c' && _settings->platformType != Settings::Win32A && _settings->platformType != Settings::Win32W && _settings->platformType != Settings::Win64 && _settings->isEnabled("portability")) 00352 invalidScanfError(tok, true); // Warn about libc bug in versions prior to 2.13-25 00353 format = false; 00354 } 00355 } 00356 } 00357 } 00358 } 00359 00360 void CheckIO::invalidScanfError(const Token *tok, bool portability) 00361 { 00362 if (portability) 00363 reportError(tok, Severity::portability, 00364 "invalidscanf", "scanf without field width limits can crash with huge input data on some versions of libc.\n" 00365 "scanf without field width limits can crash with huge input data on libc versions older than 2.13-25. Add a field " 00366 "width specifier to fix this problem:\n" 00367 " %i => %3i\n" 00368 "\n" 00369 "Sample program that can crash:\n" 00370 "\n" 00371 "#include <stdio.h>\n" 00372 "int main()\n" 00373 "{\n" 00374 " int a;\n" 00375 " scanf(\"%i\", &a);\n" 00376 " return 0;\n" 00377 "}\n" 00378 "\n" 00379 "To make it crash:\n" 00380 "perl -e 'print \"5\"x2100000' | ./a.out"); 00381 else 00382 reportError(tok, Severity::warning, 00383 "invalidscanf", "scanf without field width limits can crash with huge input data.\n" 00384 "scanf without field width limits can crash with huge input data. Add a field width " 00385 "specifier to fix this problem:\n" 00386 " %s => %20s\n" 00387 "\n" 00388 "Sample program that can crash:\n" 00389 "\n" 00390 "#include <stdio.h>\n" 00391 "int main()\n" 00392 "{\n" 00393 " char c[5];\n" 00394 " scanf(\"%s\", c);\n" 00395 " return 0;\n" 00396 "}\n" 00397 "\n" 00398 "To make it crash, type in more than 5 characters."); 00399 } 00400 00401 //--------------------------------------------------------------------------- 00402 // printf("%u", "xyz"); // Wrong argument type 00403 // printf("%u%s", 1); // Too few arguments 00404 // printf("", 1); // Too much arguments 00405 //--------------------------------------------------------------------------- 00406 static bool isComplexType(const Variable* var, const Token* varTypeTok) 00407 { 00408 if (var->type()) 00409 return(true); 00410 00411 static std::set<std::string> knownTypes; 00412 if (knownTypes.empty()) { 00413 knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type 00414 knownTypes.insert("string"); 00415 knownTypes.insert("wstring"); 00416 } 00417 00418 if (varTypeTok->str() == "std") 00419 varTypeTok = varTypeTok->tokAt(2); 00420 return((knownTypes.find(varTypeTok->str()) != knownTypes.end() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !var->isPointer() && !var->isArray()); 00421 } 00422 00423 static bool isKnownType(const Variable* var, const Token* varTypeTok) 00424 { 00425 return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok)); 00426 } 00427 00428 void CheckIO::checkWrongPrintfScanfArguments() 00429 { 00430 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00431 bool warning = _settings->isEnabled("warning"); 00432 00433 std::size_t functions = symbolDatabase->functionScopes.size(); 00434 for (std::size_t j = 0; j < functions; ++j) { 00435 const Scope * scope = symbolDatabase->functionScopes[j]; 00436 for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00437 if (!tok->isName()) continue; 00438 00439 const Token* argListTok = 0; // Points to first va_list argument 00440 std::string formatString; 00441 00442 if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%")) { 00443 formatString = tok->strAt(2); 00444 if (tok->strAt(3) == ",") { 00445 argListTok = tok->tokAt(4); 00446 } else if (tok->strAt(3) == ")") { 00447 argListTok = 0; 00448 } else { 00449 continue; 00450 } 00451 } else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%")) { 00452 const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string) 00453 if (Token::Match(formatStringTok, "%str% ,")) { 00454 argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args) 00455 formatString = formatStringTok->str(); 00456 } else if (Token::Match(formatStringTok, "%str% )")) { 00457 argListTok = 0; // Find third parameter (first argument of va_args) 00458 formatString = formatStringTok->str(); 00459 } else { 00460 continue; 00461 } 00462 } else if (Token::Match(tok, "snprintf|fnprintf|swprintf (")) { 00463 const Token* formatStringTok = tok->tokAt(2); 00464 for (int i = 0; i < 2 && formatStringTok; i++) { 00465 formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string) 00466 } 00467 if (Token::Match(formatStringTok, "%str% ,")) { 00468 argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args) 00469 formatString = formatStringTok->str(); 00470 } else if (Token::Match(formatStringTok, "%str% )")) { 00471 argListTok = 0; // Find fourth parameter (first argument of va_args) 00472 formatString = formatStringTok->str(); 00473 } else { 00474 continue; 00475 } 00476 } else { 00477 continue; 00478 } 00479 00480 // Count format string parameters.. 00481 bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf"); 00482 unsigned int numFormat = 0; 00483 bool percent = false; 00484 const Token* argListTok2 = argListTok; 00485 for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) { 00486 if (*i == '%') { 00487 percent = !percent; 00488 } else if (percent && *i == '[') { 00489 while (i != formatString.end()) { 00490 if (*i == ']') { 00491 numFormat++; 00492 if (argListTok) 00493 argListTok = argListTok->nextArgument(); 00494 percent = false; 00495 break; 00496 } 00497 ++i; 00498 } 00499 if (i == formatString.end()) 00500 break; 00501 } else if (percent) { 00502 percent = false; 00503 00504 bool _continue = false; 00505 std::string width; 00506 while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { 00507 if (*i == '*') { 00508 if (scan) 00509 _continue = true; 00510 else { 00511 numFormat++; 00512 if (argListTok) 00513 argListTok = argListTok->nextArgument(); 00514 } 00515 } else if (std::isdigit(*i)) 00516 width += *i; 00517 ++i; 00518 } 00519 if (i == formatString.end()) 00520 break; 00521 if (_continue) 00522 continue; 00523 00524 if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. 00525 numFormat++; 00526 00527 // Perform type checks 00528 if (argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern. 00529 const Variable *variableInfo = argListTok->variable(); 00530 const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL; 00531 if (varTypeTok && varTypeTok->str() == "static") 00532 varTypeTok = varTypeTok->next(); 00533 00534 if (scan && varTypeTok) { 00535 if (warning && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) 00536 invalidScanfArgTypeError(tok, tok->str(), numFormat); 00537 00538 if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1) && variableInfo->dimensions()[0].known) { 00539 if (!width.empty()) { 00540 int numWidth = std::atoi(width.c_str()); 00541 if (numWidth != (variableInfo->dimension(0) - 1)) 00542 invalidScanfFormatWidthError(tok, numFormat, numWidth, variableInfo); 00543 } 00544 } 00545 } else if (!scan && warning) { 00546 switch (*i) { 00547 case 's': 00548 if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray())) 00549 invalidPrintfArgTypeError_s(tok, numFormat); 00550 break; 00551 case 'n': 00552 if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString) 00553 invalidPrintfArgTypeError_n(tok, numFormat); 00554 break; 00555 case 'c': 00556 case 'x': 00557 case 'X': 00558 case 'o': 00559 if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) 00560 invalidPrintfArgTypeError_int(tok, numFormat, *i); 00561 else if (argListTok->type() == Token::eString) 00562 invalidPrintfArgTypeError_int(tok, numFormat, *i); 00563 break; 00564 case 'd': 00565 case 'i': 00566 if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { 00567 if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") 00568 invalidPrintfArgTypeError_sint(tok, numFormat, *i); 00569 } else if (argListTok->type() == Token::eString) 00570 invalidPrintfArgTypeError_sint(tok, numFormat, *i); 00571 break; 00572 case 'u': 00573 if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { 00574 if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") 00575 invalidPrintfArgTypeError_uint(tok, numFormat, *i); 00576 } else if (argListTok->type() == Token::eString) 00577 invalidPrintfArgTypeError_uint(tok, numFormat, *i); 00578 break; 00579 case 'p': 00580 if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) 00581 invalidPrintfArgTypeError_p(tok, numFormat); 00582 else if (argListTok->type() == Token::eString) 00583 invalidPrintfArgTypeError_p(tok, numFormat); 00584 break; 00585 case 'e': 00586 case 'E': 00587 case 'f': 00588 case 'g': 00589 case 'G': 00590 if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) 00591 invalidPrintfArgTypeError_float(tok, numFormat, *i); 00592 else if (argListTok->type() == Token::eString) 00593 invalidPrintfArgTypeError_float(tok, numFormat, *i); 00594 break; 00595 case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) 00596 case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int) 00597 // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character 00598 if (i != formatString.end() && *(i+1) == *i) { 00599 if (i+1 != formatString.end() && !isalpha(*(i+2))) { 00600 std::string modifier; 00601 modifier += *i; 00602 modifier += *(i+1); 00603 invalidLengthModifierError(tok, numFormat, modifier); 00604 } 00605 } else { 00606 if (i != formatString.end() && !isalpha(*(i+1))) { 00607 std::string modifier; 00608 modifier += *i; 00609 invalidLengthModifierError(tok, numFormat, modifier); 00610 } 00611 } 00612 break; 00613 case 'j': // intmax_t or uintmax_t 00614 case 'z': // size_t 00615 case 't': // ptrdiff_t 00616 case 'L': // long double 00617 // Expect an alphabetical character after these specifiers 00618 if (i != formatString.end() && !isalpha(*(i+1))) { 00619 std::string modifier; 00620 modifier += *i; 00621 invalidLengthModifierError(tok, numFormat, modifier); 00622 } 00623 break; 00624 default: 00625 break; 00626 } 00627 } 00628 } 00629 00630 if (argListTok) 00631 argListTok = argListTok->nextArgument(); // Find next argument 00632 } 00633 } 00634 } 00635 00636 // Count printf/scanf parameters.. 00637 unsigned int numFunction = 0; 00638 while (argListTok2) { 00639 numFunction++; 00640 argListTok2 = argListTok2->nextArgument(); // Find next argument 00641 } 00642 00643 // Mismatching number of parameters => warning 00644 if (numFormat != numFunction) 00645 wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction); 00646 } 00647 } 00648 } 00649 00650 void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, 00651 const std::string &functionName, 00652 unsigned int numFormat, 00653 unsigned int numFunction) 00654 { 00655 Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; 00656 if (severity != Severity::error && !_settings->isEnabled("style")) 00657 return; 00658 00659 std::ostringstream errmsg; 00660 errmsg << functionName 00661 << " format string has " 00662 << numFormat 00663 << " parameters but " 00664 << (numFormat > numFunction ? "only " : "") 00665 << numFunction 00666 << " are given."; 00667 00668 reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str()); 00669 } 00670 00671 void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat) 00672 { 00673 std::ostringstream errmsg; 00674 errmsg << functionName << " argument no. " << numFormat << ": requires a non-const pointer or array as argument."; 00675 reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str()); 00676 } 00677 void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat) 00678 { 00679 std::ostringstream errmsg; 00680 errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list."; 00681 reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str()); 00682 } 00683 void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat) 00684 { 00685 std::ostringstream errmsg; 00686 errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list."; 00687 reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str()); 00688 } 00689 void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat) 00690 { 00691 std::ostringstream errmsg; 00692 errmsg << "%p in format string (no. " << numFormat << ") requires an address given in the argument list."; 00693 reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); 00694 } 00695 void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c) 00696 { 00697 std::ostringstream errmsg; 00698 errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list."; 00699 reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); 00700 } 00701 void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c) 00702 { 00703 std::ostringstream errmsg; 00704 errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list."; 00705 reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str()); 00706 } 00707 void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c) 00708 { 00709 std::ostringstream errmsg; 00710 errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list."; 00711 reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str()); 00712 } 00713 void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c) 00714 { 00715 std::ostringstream errmsg; 00716 errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list."; 00717 reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); 00718 } 00719 void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier) 00720 { 00721 std::ostringstream errmsg; 00722 errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; 00723 reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str()); 00724 } 00725 00726 void CheckIO::invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var) 00727 { 00728 std::ostringstream errmsg; 00729 Severity::SeverityType severity = Severity::warning; 00730 bool inconclusive = false; 00731 00732 if (var) { 00733 if (var->dimension(0) > width) { 00734 if (!_settings->inconclusive) 00735 return; 00736 inconclusive = true; 00737 errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer" 00738 << " '" << var->name() << "[" << var->dimension(0) << "]'."; 00739 } else { 00740 errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '" 00741 << var->name() << "[" << var->dimension(0) << "]', use %" << (var->dimension(0) - 1) << "s to prevent overflowing it."; 00742 severity = Severity::error; 00743 } 00744 00745 } else 00746 errmsg << "Width " << width << " given in format string (no. " << numFormat << ") doesn't match destination buffer."; 00747 00748 if (severity == Severity::error || _settings->isEnabled("style")) 00749 reportError(tok, severity, "invalidScanfFormatWidth", errmsg.str(), inconclusive); 00750 }
1.7.6.1