Splitted CheckIO from CheckOther.
This commit is contained in:
parent
892c125ff8
commit
b81eafe0dc
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#include "checkio.h"
|
||||
|
||||
#include "tokenize.h"
|
||||
#include "token.h"
|
||||
#include "errorlogger.h"
|
||||
#include "symboldatabase.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Register CheckIO..
|
||||
namespace {
|
||||
CheckIO instance;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// std::cout << std::cout;
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckIO::checkCoutCerrMisusage()
|
||||
{
|
||||
bool firstCout = false;
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
if (tok->str() == "(")
|
||||
tok = tok->link();
|
||||
|
||||
if (Token::Match(tok, "std :: cout|cerr")) {
|
||||
if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") {
|
||||
coutCerrMisusageError(tok, tok->strAt(2));
|
||||
firstCout = false;
|
||||
} else if (tok->strAt(3) == "<<")
|
||||
firstCout = true;
|
||||
} else if (firstCout && tok->str() == ";")
|
||||
firstCout = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName)
|
||||
{
|
||||
reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// fflush(stdin) <- fflush only applies to output streams in ANSI C
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckIO::checkFflushOnInputStream()
|
||||
{
|
||||
const Token *tok = _tokenizer->tokens();
|
||||
while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) {
|
||||
fflushOnInputStreamError(tok, tok->strAt(2));
|
||||
tok = tok->tokAt(4);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error,
|
||||
"fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// scanf without field width limits can crash with huge input data
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckIO::invalidScanf()
|
||||
{
|
||||
if (!_settings->isEnabled("style"))
|
||||
return;
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
const Token *formatToken = 0;
|
||||
if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
|
||||
formatToken = tok->tokAt(2);
|
||||
else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
|
||||
const Token* nextArg = tok->tokAt(2)->nextArgument();
|
||||
if (nextArg && nextArg->type() == Token::eString)
|
||||
formatToken = nextArg;
|
||||
else
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
|
||||
bool format = false;
|
||||
|
||||
// scan the string backwards, so we dont need to keep states
|
||||
const std::string &formatstr(formatToken->str());
|
||||
for (unsigned int i = 1; i < formatstr.length(); i++) {
|
||||
if (formatstr[i] == '%')
|
||||
format = !format;
|
||||
|
||||
else if (!format)
|
||||
continue;
|
||||
|
||||
else if (std::isdigit(formatstr[i])) {
|
||||
format = false;
|
||||
}
|
||||
|
||||
else if (std::isalpha(formatstr[i])) {
|
||||
if (formatstr[i] != 'c') // #3490 - field width limits are not necessary for %c
|
||||
invalidScanfError(tok);
|
||||
format = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIO::invalidScanfError(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"invalidscanf", "scanf without field width limits can crash with huge input data\n"
|
||||
"scanf without field width limits can crash with huge input data. To fix this error "
|
||||
"message add a field width specifier:\n"
|
||||
" %s => %20s\n"
|
||||
" %i => %3i\n"
|
||||
"\n"
|
||||
"Sample program that can crash:\n"
|
||||
"\n"
|
||||
"#include <stdio.h>\n"
|
||||
"int main()\n"
|
||||
"{\n"
|
||||
" int a;\n"
|
||||
" scanf(\"%i\", &a);\n"
|
||||
" return 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"To make it crash:\n"
|
||||
"perl -e 'print \"5\"x2100000' | ./a.out");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// printf("%u", "xyz"); // Wrong argument type
|
||||
// printf("%u%s", 1); // Too few arguments
|
||||
// printf("", 1); // Too much arguments
|
||||
//---------------------------------------------------------------------------
|
||||
static bool isComplexType(const Variable* var, const Token* varTypeTok)
|
||||
{
|
||||
if (var->type())
|
||||
return(true);
|
||||
|
||||
static std::set<std::string> knownTypes;
|
||||
if (knownTypes.empty()) {
|
||||
knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type
|
||||
knownTypes.insert("string");
|
||||
}
|
||||
|
||||
if (varTypeTok->str() == "std")
|
||||
varTypeTok = varTypeTok->tokAt(2);
|
||||
return(knownTypes.find(varTypeTok->str()) != knownTypes.end() && !var->isPointer() && !var->isArray());
|
||||
}
|
||||
|
||||
static bool isKnownType(const Variable* var, const Token* varTypeTok)
|
||||
{
|
||||
return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok));
|
||||
}
|
||||
|
||||
void CheckIO::checkWrongPrintfScanfArguments()
|
||||
{
|
||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
if (!tok->isName()) continue;
|
||||
|
||||
const Token* argListTok = 0; // Points to first va_list argument
|
||||
std::string formatString;
|
||||
|
||||
if (Token::Match(tok, "printf|scanf ( %str%")) {
|
||||
formatString = tok->strAt(2);
|
||||
if (tok->strAt(3) == ",") {
|
||||
argListTok = tok->tokAt(4);
|
||||
} else if (tok->strAt(3) == ")") {
|
||||
argListTok = 0;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf ( %any%")) {
|
||||
const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string)
|
||||
if (Token::Match(formatStringTok, "%str% ,")) {
|
||||
argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else if (Token::Match(formatStringTok, "%str% )")) {
|
||||
argListTok = 0; // Find third parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (Token::Match(tok, "snprintf|fnprintf (")) {
|
||||
const Token* formatStringTok = tok->tokAt(2);
|
||||
for (int i = 0; i < 2 && formatStringTok; i++) {
|
||||
formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string)
|
||||
}
|
||||
if (Token::Match(formatStringTok, "%str% ,")) {
|
||||
argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else if (Token::Match(formatStringTok, "%str% )")) {
|
||||
argListTok = 0; // Find fourth parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count format string parameters..
|
||||
bool scan = Token::Match(tok, "sscanf|fscanf|scanf");
|
||||
unsigned int numFormat = 0;
|
||||
bool percent = false;
|
||||
const Token* argListTok2 = argListTok;
|
||||
for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) {
|
||||
if (*i == '%') {
|
||||
percent = !percent;
|
||||
} else if (percent && *i == '[') {
|
||||
while (i != formatString.end()) {
|
||||
if (*i == ']') {
|
||||
numFormat++;
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument();
|
||||
percent = false;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == formatString.end())
|
||||
break;
|
||||
} else if (percent) {
|
||||
percent = false;
|
||||
|
||||
bool _continue = false;
|
||||
while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
|
||||
if (*i == '*') {
|
||||
if (scan)
|
||||
_continue = true;
|
||||
else {
|
||||
numFormat++;
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument();
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == formatString.end())
|
||||
break;
|
||||
if (_continue)
|
||||
continue;
|
||||
|
||||
if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
|
||||
numFormat++;
|
||||
|
||||
// Perform type checks
|
||||
if (_settings->isEnabled("style") && argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern.
|
||||
const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId());
|
||||
const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
|
||||
if (varTypeTok && varTypeTok->str() == "static")
|
||||
varTypeTok = varTypeTok->next();
|
||||
|
||||
if (scan && varTypeTok) {
|
||||
if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")
|
||||
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
||||
} else if (!scan) {
|
||||
switch (*i) {
|
||||
case 's':
|
||||
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_s(tok, numFormat);
|
||||
break;
|
||||
case 'n':
|
||||
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")) || argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_n(tok, numFormat);
|
||||
break;
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| bool|short|long|int|char|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, *i);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, *i);
|
||||
break;
|
||||
case 'p':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| short|long|int|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray())
|
||||
invalidPrintfArgTypeError_p(tok, numFormat);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_p(tok, numFormat);
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument(); // Find next argument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count printf/scanf parameters..
|
||||
unsigned int numFunction = 0;
|
||||
while (argListTok2) {
|
||||
numFunction++;
|
||||
argListTok2 = argListTok2->nextArgument(); // Find next argument
|
||||
}
|
||||
|
||||
// Mismatching number of parameters => warning
|
||||
if (numFormat != numFunction)
|
||||
wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
|
||||
const std::string &functionName,
|
||||
unsigned int numFormat,
|
||||
unsigned int numFunction)
|
||||
{
|
||||
Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning;
|
||||
if (severity != Severity::error && !_settings->isEnabled("style"))
|
||||
return;
|
||||
|
||||
std::ostringstream errmsg;
|
||||
errmsg << functionName
|
||||
<< " format string has "
|
||||
<< numFormat
|
||||
<< " parameters but "
|
||||
<< (numFormat > numFunction ? "only " : "")
|
||||
<< numFunction
|
||||
<< " are given";
|
||||
|
||||
reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str());
|
||||
}
|
||||
|
||||
void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << functionName << " argument no. " << numFormat << ": requires non-const pointers or arrays as arguments";
|
||||
reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%p in format string (no. " << numFormat << ") requires an integer or pointer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str());
|
||||
}
|
||||
void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#ifndef CheckIOH
|
||||
#define CheckIOH
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "check.h"
|
||||
|
||||
/// @addtogroup Checks
|
||||
/// @{
|
||||
|
||||
|
||||
/** @brief %Check input output operations. */
|
||||
class CheckIO : public Check {
|
||||
public:
|
||||
/** @brief This constructor is used when registering CheckIO */
|
||||
CheckIO() : Check(myName())
|
||||
{ }
|
||||
|
||||
/** @brief This constructor is used when running checks. */
|
||||
CheckIO(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
|
||||
: Check(myName(), tokenizer, settings, errorLogger)
|
||||
{ }
|
||||
|
||||
/** @brief Run checks on the normal token list */
|
||||
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
|
||||
CheckIO checkIO(tokenizer, settings, errorLogger);
|
||||
|
||||
checkIO.checkWrongPrintfScanfArguments();
|
||||
}
|
||||
|
||||
/** @brief Run checks on the simplified token list */
|
||||
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
|
||||
CheckIO checkIO(tokenizer, settings, errorLogger);
|
||||
|
||||
checkIO.checkCoutCerrMisusage();
|
||||
checkIO.checkFflushOnInputStream();
|
||||
checkIO.invalidScanf();
|
||||
}
|
||||
|
||||
/** @brief %Check for missusage of std::cout */
|
||||
void checkCoutCerrMisusage();
|
||||
|
||||
/** @brief %Check for using fflush() on an input stream*/
|
||||
void checkFflushOnInputStream();
|
||||
|
||||
/** @brief scanf can crash if width specifiers are not used */
|
||||
void invalidScanf();
|
||||
|
||||
/** @brief %Checks type and number of arguments given to functions like printf or scanf*/
|
||||
void checkWrongPrintfScanfArguments();
|
||||
|
||||
private:
|
||||
// Reporting errors..
|
||||
void coutCerrMisusageError(const Token* tok, const std::string& streamName);
|
||||
void fflushOnInputStreamError(const Token *tok, const std::string &varname);
|
||||
void invalidScanfError(const Token *tok);
|
||||
void wrongPrintfScanfArgumentsError(const Token* tok,
|
||||
const std::string &function,
|
||||
unsigned int numFormat,
|
||||
unsigned int numFunction);
|
||||
void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c);
|
||||
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
||||
|
||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||
CheckIO c(0, settings, errorLogger);
|
||||
|
||||
c.coutCerrMisusageError(0, "cout");
|
||||
c.fflushOnInputStreamError(0, "stdin");
|
||||
c.invalidScanfError(0);
|
||||
c.wrongPrintfScanfArgumentsError(0,"printf",3,2);
|
||||
c.invalidScanfArgTypeError(0, "scanf", 1);
|
||||
c.invalidPrintfArgTypeError_s(0, 1);
|
||||
c.invalidPrintfArgTypeError_n(0, 1);
|
||||
c.invalidPrintfArgTypeError_p(0, 1);
|
||||
c.invalidPrintfArgTypeError_int(0, 1, 'u');
|
||||
c.invalidPrintfArgTypeError_float(0, 1, 'f');
|
||||
}
|
||||
|
||||
std::string myName() const {
|
||||
return "IO";
|
||||
}
|
||||
|
||||
std::string classInfo() const {
|
||||
return "Check input/output operations.\n";
|
||||
"* Bad usage of the function 'sprintf' (overlapping data)\n"
|
||||
"* Using fflush() on an input stream\n"
|
||||
"* Invalid usage of output stream. For example: std::cout << std::cout;'\n"
|
||||
"* Wrong number of arguments given to 'printf' or 'scanf;'\n";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
|
@ -22,7 +22,6 @@
|
|||
#include "mathlib.h"
|
||||
#include "symboldatabase.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cmath> // fabs()
|
||||
#include <stack>
|
||||
#include <algorithm> // find_if()
|
||||
|
@ -423,24 +422,6 @@ void CheckOther::invalidPointerCastError(const Token* tok, const std::string& fr
|
|||
reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// fflush(stdin) <- fflush only applies to output streams in ANSI C
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckOther::checkFflushOnInputStream()
|
||||
{
|
||||
const Token *tok = _tokenizer->tokens();
|
||||
while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) {
|
||||
fflushOnInputStreamError(tok, tok->strAt(2));
|
||||
tok = tok->tokAt(4);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOther::fflushOnInputStreamError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error,
|
||||
"fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckOther::checkSizeofForNumericParameter()
|
||||
|
@ -859,32 +840,6 @@ void CheckOther::switchCaseFallThrough(const Token *tok)
|
|||
"switchCaseFallThrough", "Switch falls through case without comment");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// std::cout << std::cout;
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckOther::checkCoutCerrMisusage()
|
||||
{
|
||||
bool firstCout = false;
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
if (tok->str() == "(")
|
||||
tok = tok->link();
|
||||
|
||||
if (Token::Match(tok, "std :: cout|cerr")) {
|
||||
if (firstCout && tok->strAt(-1) == "<<" && tok->strAt(3) != ".") {
|
||||
coutCerrMisusageError(tok, tok->strAt(2));
|
||||
firstCout = false;
|
||||
} else if (tok->strAt(3) == "<<")
|
||||
firstCout = true;
|
||||
} else if (firstCout && tok->str() == ";")
|
||||
firstCout = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOther::coutCerrMisusageError(const Token* tok, const std::string& streamName)
|
||||
{
|
||||
reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// int x = 1;
|
||||
// x = x; // <- redundant assignment to self
|
||||
|
@ -1330,328 +1285,6 @@ void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string
|
|||
"to sprintf() or snprintf(), the results are undefined.'");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------
|
||||
void CheckOther::invalidScanf()
|
||||
{
|
||||
if (!_settings->isEnabled("style"))
|
||||
return;
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
const Token *formatToken = 0;
|
||||
if (Token::Match(tok, "scanf|vscanf ( %str% ,"))
|
||||
formatToken = tok->tokAt(2);
|
||||
else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) {
|
||||
const Token* nextArg = tok->tokAt(2)->nextArgument();
|
||||
if (nextArg && nextArg->type() == Token::eString)
|
||||
formatToken = nextArg;
|
||||
else
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
|
||||
bool format = false;
|
||||
|
||||
// scan the string backwards, so we dont need to keep states
|
||||
const std::string &formatstr(formatToken->str());
|
||||
for (unsigned int i = 1; i < formatstr.length(); i++) {
|
||||
if (formatstr[i] == '%')
|
||||
format = !format;
|
||||
|
||||
else if (!format)
|
||||
continue;
|
||||
|
||||
else if (std::isdigit(formatstr[i])) {
|
||||
format = false;
|
||||
}
|
||||
|
||||
else if (std::isalpha(formatstr[i])) {
|
||||
if (formatstr[i] != 'c') // #3490 - field width limits are not necessary for %c
|
||||
invalidScanfError(tok);
|
||||
format = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOther::invalidScanfError(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"invalidscanf", "scanf without field width limits can crash with huge input data\n"
|
||||
"scanf without field width limits can crash with huge input data. To fix this error "
|
||||
"message add a field width specifier:\n"
|
||||
" %s => %20s\n"
|
||||
" %i => %3i\n"
|
||||
"\n"
|
||||
"Sample program that can crash:\n"
|
||||
"\n"
|
||||
"#include <stdio.h>\n"
|
||||
"int main()\n"
|
||||
"{\n"
|
||||
" int a;\n"
|
||||
" scanf(\"%i\", &a);\n"
|
||||
" return 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"To make it crash:\n"
|
||||
"perl -e 'print \"5\"x2100000' | ./a.out");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// printf("%u", "xyz"); // Wrong argument type
|
||||
// printf("%u%s", 1); // Too few arguments
|
||||
// printf("", 1); // Too much arguments
|
||||
//---------------------------------------------------------------------------
|
||||
static bool isComplexType(const Variable* var, const Token* varTypeTok)
|
||||
{
|
||||
if (var->type())
|
||||
return(true);
|
||||
|
||||
static std::set<std::string> knownTypes;
|
||||
if (knownTypes.empty()) {
|
||||
knownTypes.insert("struct"); // If a type starts with the struct keyword, its a complex type
|
||||
knownTypes.insert("string");
|
||||
}
|
||||
|
||||
if (varTypeTok->str() == "std")
|
||||
varTypeTok = varTypeTok->tokAt(2);
|
||||
return(knownTypes.find(varTypeTok->str()) != knownTypes.end() && !var->isPointer() && !var->isArray());
|
||||
}
|
||||
|
||||
static bool isKnownType(const Variable* var, const Token* varTypeTok)
|
||||
{
|
||||
return(varTypeTok->isStandardType() || varTypeTok->next()->isStandardType() || isComplexType(var, varTypeTok));
|
||||
}
|
||||
|
||||
void CheckOther::checkWrongPrintfScanfArguments()
|
||||
{
|
||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
|
||||
if (!tok->isName()) continue;
|
||||
|
||||
const Token* argListTok = 0; // Points to first va_list argument
|
||||
std::string formatString;
|
||||
|
||||
if (Token::Match(tok, "printf|scanf ( %str%")) {
|
||||
formatString = tok->strAt(2);
|
||||
if (tok->strAt(3) == ",") {
|
||||
argListTok = tok->tokAt(4);
|
||||
} else if (tok->strAt(3) == ")") {
|
||||
argListTok = 0;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf ( %any%")) {
|
||||
const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string)
|
||||
if (Token::Match(formatStringTok, "%str% ,")) {
|
||||
argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else if (Token::Match(formatStringTok, "%str% )")) {
|
||||
argListTok = 0; // Find third parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (Token::Match(tok, "snprintf|fnprintf (")) {
|
||||
const Token* formatStringTok = tok->tokAt(2);
|
||||
for (int i = 0; i < 2 && formatStringTok; i++) {
|
||||
formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string)
|
||||
}
|
||||
if (Token::Match(formatStringTok, "%str% ,")) {
|
||||
argListTok = formatStringTok->nextArgument(); // Find fourth parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else if (Token::Match(formatStringTok, "%str% )")) {
|
||||
argListTok = 0; // Find fourth parameter (first argument of va_args)
|
||||
formatString = formatStringTok->str();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count format string parameters..
|
||||
bool scan = Token::Match(tok, "sscanf|fscanf|scanf");
|
||||
unsigned int numFormat = 0;
|
||||
bool percent = false;
|
||||
const Token* argListTok2 = argListTok;
|
||||
for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) {
|
||||
if (*i == '%') {
|
||||
percent = !percent;
|
||||
} else if (percent && *i == '[') {
|
||||
while (i != formatString.end()) {
|
||||
if (*i == ']') {
|
||||
numFormat++;
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument();
|
||||
percent = false;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == formatString.end())
|
||||
break;
|
||||
} else if (percent) {
|
||||
percent = false;
|
||||
|
||||
bool _continue = false;
|
||||
while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
|
||||
if (*i == '*') {
|
||||
if (scan)
|
||||
_continue = true;
|
||||
else {
|
||||
numFormat++;
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument();
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (i == formatString.end())
|
||||
break;
|
||||
if (_continue)
|
||||
continue;
|
||||
|
||||
if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions.
|
||||
numFormat++;
|
||||
|
||||
// Perform type checks
|
||||
if (_settings->isEnabled("style") && argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern.
|
||||
const Variable* variableInfo = symbolDatabase->getVariableFromVarId(argListTok->varId());
|
||||
const Token* varTypeTok = variableInfo ? variableInfo->typeStartToken() : NULL;
|
||||
if (varTypeTok && varTypeTok->str() == "static")
|
||||
varTypeTok = varTypeTok->next();
|
||||
|
||||
if (scan && varTypeTok) {
|
||||
if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")
|
||||
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
||||
} else if (!scan) {
|
||||
switch (*i) {
|
||||
case 's':
|
||||
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_s(tok, numFormat);
|
||||
break;
|
||||
case 'n':
|
||||
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->str() == "const")) || argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_n(tok, numFormat);
|
||||
break;
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| bool|short|long|int|char|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, *i);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_int(tok, numFormat, *i);
|
||||
break;
|
||||
case 'p':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "unsigned|signed| short|long|int|size_t|unsigned|signed") && !variableInfo->isPointer() && !variableInfo->isArray())
|
||||
invalidPrintfArgTypeError_p(tok, numFormat);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_p(tok, numFormat);
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (varTypeTok && varTypeTok->str() == "const")
|
||||
varTypeTok = varTypeTok->next();
|
||||
if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray()))
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
||||
else if (argListTok->type() == Token::eString)
|
||||
invalidPrintfArgTypeError_float(tok, numFormat, *i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argListTok)
|
||||
argListTok = argListTok->nextArgument(); // Find next argument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count printf/scanf parameters..
|
||||
unsigned int numFunction = 0;
|
||||
while (argListTok2) {
|
||||
numFunction++;
|
||||
argListTok2 = argListTok2->nextArgument(); // Find next argument
|
||||
}
|
||||
|
||||
// Mismatching number of parameters => warning
|
||||
if (numFormat != numFunction)
|
||||
wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOther::wrongPrintfScanfArgumentsError(const Token* tok,
|
||||
const std::string &functionName,
|
||||
unsigned int numFormat,
|
||||
unsigned int numFunction)
|
||||
{
|
||||
Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning;
|
||||
if (severity != Severity::error && !_settings->isEnabled("style"))
|
||||
return;
|
||||
|
||||
std::ostringstream errmsg;
|
||||
errmsg << functionName
|
||||
<< " format string has "
|
||||
<< numFormat
|
||||
<< " parameters but "
|
||||
<< (numFormat > numFunction ? "only " : "")
|
||||
<< numFunction
|
||||
<< " are given";
|
||||
|
||||
reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str());
|
||||
}
|
||||
|
||||
void CheckOther::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << functionName << " argument no. " << numFormat << ": requires non-const pointers or arrays as arguments";
|
||||
reportError(tok, Severity::warning, "invalidScanfArgType", errmsg.str());
|
||||
}
|
||||
void CheckOther::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
|
||||
}
|
||||
void CheckOther::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%n in format string (no. " << numFormat << ") requires a pointer to an non-const integer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_n", errmsg.str());
|
||||
}
|
||||
void CheckOther::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%p in format string (no. " << numFormat << ") requires an integer or pointer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str());
|
||||
}
|
||||
void CheckOther::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str());
|
||||
}
|
||||
void CheckOther::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list";
|
||||
reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// if (!x==3) <- Probably meant to be "x!=3"
|
||||
//---------------------------------------------------------------------------
|
||||
|
|
|
@ -68,7 +68,6 @@ public:
|
|||
checkOther.checkDuplicateExpression();
|
||||
checkOther.checkUnreachableCode();
|
||||
checkOther.checkSuspiciousSemicolon();
|
||||
checkOther.checkWrongPrintfScanfArguments();
|
||||
checkOther.checkVariableScope();
|
||||
checkOther.clarifyCondition(); // not simplified because ifAssign
|
||||
checkOther.checkComparisonOfBoolExpressionWithInt();
|
||||
|
@ -88,10 +87,7 @@ public:
|
|||
checkOther.checkZeroDivision();
|
||||
checkOther.checkMathFunctions();
|
||||
checkOther.checkCCTypeFunctions();
|
||||
checkOther.checkFflushOnInputStream();
|
||||
checkOther.invalidScanf();
|
||||
|
||||
checkOther.checkCoutCerrMisusage();
|
||||
checkOther.checkIncorrectLogicOperator();
|
||||
checkOther.checkMisusedScopedObject();
|
||||
checkOther.checkMemsetZeroBytes();
|
||||
|
@ -157,30 +153,18 @@ public:
|
|||
|
||||
void lookupVar(const Token *tok1, const std::string &varname);
|
||||
|
||||
/** @brief %Check for using fflush() on an input stream*/
|
||||
void checkFflushOnInputStream();
|
||||
|
||||
/** @brief %Check for 'sizeof sizeof ..' */
|
||||
void sizeofsizeof();
|
||||
|
||||
/** @brief %Check for calculations inside sizeof */
|
||||
void sizeofCalculation();
|
||||
|
||||
/** @brief scanf can crash if width specifiers are not used */
|
||||
void invalidScanf();
|
||||
|
||||
/** @brief %Checks type and number of arguments given to functions like printf or scanf*/
|
||||
void checkWrongPrintfScanfArguments();
|
||||
|
||||
/** @brief %Check for assigning to the same variable twice in a switch statement*/
|
||||
void checkRedundantAssignmentInSwitch();
|
||||
|
||||
/** @brief %Check for switch case fall through without comment */
|
||||
void checkSwitchCaseFallThrough();
|
||||
|
||||
/** @brief %Check for missusage of std::cout */
|
||||
void checkCoutCerrMisusage();
|
||||
|
||||
/** @brief %Check for assigning a variable to itself*/
|
||||
void checkSelfAssignment();
|
||||
|
||||
|
@ -256,17 +240,6 @@ private:
|
|||
void clarifyConditionError(const Token *tok, bool assign, bool boolop);
|
||||
void sizeofsizeofError(const Token *tok);
|
||||
void sizeofCalculationError(const Token *tok, bool inconclusive);
|
||||
void invalidScanfError(const Token *tok);
|
||||
void wrongPrintfScanfArgumentsError(const Token* tok,
|
||||
const std::string &function,
|
||||
unsigned int numFormat,
|
||||
unsigned int numFunction);
|
||||
void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat);
|
||||
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c);
|
||||
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
||||
void cstyleCastError(const Token *tok);
|
||||
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
|
||||
void dangerousUsageStrtolError(const Token *tok);
|
||||
|
@ -279,10 +252,8 @@ private:
|
|||
void variableScopeError(const Token *tok, const std::string &varname);
|
||||
void strPlusCharError(const Token *tok);
|
||||
void zerodivError(const Token *tok);
|
||||
void coutCerrMisusageError(const Token* tok, const std::string& streamName);
|
||||
void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1);
|
||||
void cctypefunctionCallError(const Token *tok, const std::string &functionName, const std::string &value);
|
||||
void fflushOnInputStreamError(const Token *tok, const std::string &varname);
|
||||
void redundantAssignmentInSwitchError(const Token *tok, const std::string &varname);
|
||||
void redundantStrcpyInSwitchError(const Token *tok, const std::string &varname);
|
||||
void switchCaseFallThrough(const Token *tok);
|
||||
|
@ -325,12 +296,10 @@ private:
|
|||
c.udivError(0, false);
|
||||
c.zerodivError(0);
|
||||
c.mathfunctionCallError(0);
|
||||
c.fflushOnInputStreamError(0, "stdin");
|
||||
c.misusedScopeObjectError(NULL, "varname");
|
||||
c.sizeofForArrayParameterError(0);
|
||||
c.sizeofForPointerError(0, "varname");
|
||||
c.sizeofForNumericParameterError(0);
|
||||
c.coutCerrMisusageError(0, "cout");
|
||||
c.doubleFreeError(0, "varname");
|
||||
c.invalidPointerCastError(0, "float", "double", false);
|
||||
|
||||
|
@ -349,7 +318,6 @@ private:
|
|||
c.switchCaseFallThrough(0);
|
||||
c.selfAssignmentError(0, "varname");
|
||||
c.assignmentInAssertError(0, "varname");
|
||||
c.invalidScanfError(0);
|
||||
c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true);
|
||||
c.redundantConditionError(0, "If x > 10 the condition x > 11 is always true.");
|
||||
c.memsetZeroBytesError(0, "varname");
|
||||
|
@ -371,13 +339,6 @@ private:
|
|||
c.bitwiseOnBooleanError(0, "varname", "&&");
|
||||
c.comparisonOfBoolExpressionWithIntError(0, true);
|
||||
c.SuspiciousSemicolonError(0);
|
||||
c.wrongPrintfScanfArgumentsError(0,"printf",3,2);
|
||||
c.invalidScanfArgTypeError(0, "scanf", 1);
|
||||
c.invalidPrintfArgTypeError_s(0, 1);
|
||||
c.invalidPrintfArgTypeError_n(0, 1);
|
||||
c.invalidPrintfArgTypeError_p(0, 1);
|
||||
c.invalidPrintfArgTypeError_int(0, 1, 'u');
|
||||
c.invalidPrintfArgTypeError_float(0, 1, 'f');
|
||||
c.cctypefunctionCallError(0, "funname", "value");
|
||||
c.moduloAlwaysTrueFalseError(0, "1");
|
||||
}
|
||||
|
@ -391,17 +352,13 @@ private:
|
|||
|
||||
// error
|
||||
"* Assigning bool value to pointer (converting bool value to address)\n"
|
||||
"* [[OverlappingData|bad usage of the function 'sprintf' (overlapping data)]]\n"
|
||||
"* division with zero\n"
|
||||
"* using fflush() on an input stream\n"
|
||||
"* scoped object destroyed immediately after construction\n"
|
||||
"* assignment in an assert statement\n"
|
||||
"* sizeof for array given as function argument\n"
|
||||
"* sizeof for numeric given as function argument\n"
|
||||
"* using sizeof(pointer) instead of the size of pointed data\n"
|
||||
"* incorrect length arguments for 'substr' and 'strncmp'\n"
|
||||
"* invalid usage of output stream. For example: std::cout << std::cout;'\n"
|
||||
"* wrong number of arguments given to 'printf' or 'scanf;'\n"
|
||||
"* double free() or double closedir()\n"
|
||||
|
||||
// style
|
||||
|
@ -437,10 +394,7 @@ private:
|
|||
"* using bool in bitwise expression\n"
|
||||
"* Suspicious use of ; at the end of 'if/for/while' statement.\n"
|
||||
"* incorrect usage of functions from ctype library.\n"
|
||||
"* Comparisons of modulo results that are always true/false.\n"
|
||||
|
||||
// optimisations
|
||||
"* optimisation: detect post increment/decrement\n";
|
||||
"* Comparisons of modulo results that are always true/false.\n";
|
||||
}
|
||||
|
||||
void checkExpressionRange(const std::list<Function> &constFunctions,
|
||||
|
|
|
@ -74,8 +74,6 @@ private:
|
|||
TEST_CASE(mathfunctionCall1);
|
||||
TEST_CASE(cctypefunctionCall);
|
||||
|
||||
TEST_CASE(fflushOnInputStreamTest);
|
||||
|
||||
TEST_CASE(sizeofsizeof);
|
||||
TEST_CASE(sizeofCalculation);
|
||||
|
||||
|
@ -83,17 +81,7 @@ private:
|
|||
TEST_CASE(switchFallThroughCase);
|
||||
TEST_CASE(unreachableCode);
|
||||
|
||||
TEST_CASE(coutCerrMisusage);
|
||||
|
||||
TEST_CASE(selfAssignment);
|
||||
TEST_CASE(testScanf1);
|
||||
TEST_CASE(testScanf2);
|
||||
TEST_CASE(testScanf3);
|
||||
TEST_CASE(testScanf4);
|
||||
|
||||
TEST_CASE(testScanfArgument);
|
||||
TEST_CASE(testPrintfArgument);
|
||||
|
||||
TEST_CASE(trac1132);
|
||||
TEST_CASE(testMisusedScopeObjectDoesNotPickFunction1);
|
||||
TEST_CASE(testMisusedScopeObjectDoesNotPickFunction2);
|
||||
|
@ -1274,19 +1262,6 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:2]: (error) Passing value -10000 to isgraph() cause undefined behavior, which may lead to a crash\n", errout.str());
|
||||
|
||||
}
|
||||
void fflushOnInputStreamTest() {
|
||||
check("void foo()\n"
|
||||
"{\n"
|
||||
" fflush(stdin);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (error) fflush() called on input stream \"stdin\" may result in undefined behaviour\n", errout.str());
|
||||
|
||||
check("void foo()\n"
|
||||
"{\n"
|
||||
" fflush(stdout);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void sizeofsizeof() {
|
||||
check("void foo()\n"
|
||||
|
@ -2136,232 +2111,6 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void testScanf1() {
|
||||
check("#include <stdio.h>\n"
|
||||
"int main(int argc, char **argv)\n"
|
||||
"{\n"
|
||||
" int a, b;\n"
|
||||
" FILE *file = fopen(\"test\", \"r\");\n"
|
||||
" b = fscanf(file, \"aa %ds\", &a);\n"
|
||||
" c = scanf(\"aa %ds\", &a);\n"
|
||||
" b = fscanf(file, \"aa%%ds\", &a);\n"
|
||||
" fclose(file);\n"
|
||||
" return b;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:8]: (warning) fscanf format string has 0 parameters but 1 are given\n"
|
||||
"[test.cpp:6]: (warning) scanf without field width limits can crash with huge input data\n"
|
||||
"[test.cpp:7]: (warning) scanf without field width limits can crash with huge input data\n", errout.str());
|
||||
}
|
||||
|
||||
void testScanf2() {
|
||||
check("#include <stdio.h>\n"
|
||||
"int main(int argc, char **argv)\n"
|
||||
"{\n"
|
||||
" int a, b;\n"
|
||||
" FILE *file = fopen(\"test\", \"r\");\n"
|
||||
" b = fscanf(file, \"aa%%%ds\", &a);\n"
|
||||
" c = scanf(\"aa %%%ds\", &a);\n"
|
||||
" b = fscanf(file, \"aa%%ds\", &a);\n"
|
||||
" fclose(file);\n"
|
||||
" return b;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:8]: (warning) fscanf format string has 0 parameters but 1 are given\n"
|
||||
"[test.cpp:6]: (warning) scanf without field width limits can crash with huge input data\n"
|
||||
"[test.cpp:7]: (warning) scanf without field width limits can crash with huge input data\n", errout.str());
|
||||
}
|
||||
|
||||
void testScanf3() {
|
||||
check("#include <stdio.h>\n"
|
||||
"int main(int argc, char **argv)\n"
|
||||
"{\n"
|
||||
" char a[32];\n"
|
||||
" int b, c;\n"
|
||||
" FILE *file = fopen(\"test\", \"r\");\n"
|
||||
" c = fscanf(file, \"%[^ ] %d\n\", a, &b);\n"
|
||||
" fclose(file);\n"
|
||||
" return c;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("#include <stdio.h>\n"
|
||||
"int main(int argc, char **argv)\n"
|
||||
"{\n"
|
||||
" char a[32];\n"
|
||||
" int b;\n"
|
||||
" FILE *file = fopen(\"test\", \"r\");\n"
|
||||
" b = fscanf(file, \"%[^ \n\", a);\n"
|
||||
" fclose(file);\n"
|
||||
" return b;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:7]: (warning) fscanf format string has 0 parameters but 1 are given\n", errout.str());
|
||||
}
|
||||
|
||||
void testScanf4() {
|
||||
check("void f() {\n"
|
||||
" char c;\n"
|
||||
" scanf(\"%c\", &c);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void testScanfArgument() {
|
||||
check("void foo() {\n"
|
||||
" scanf(\"%1d\", &foo);\n"
|
||||
" sscanf(bar, \"%1d\", &foo);\n"
|
||||
" scanf(\"%1u%1u\", &foo, bar());\n"
|
||||
" scanf(\"%*1x %1x %29s\", &count, KeyName);\n" // #3373
|
||||
" fscanf(f, \"%7ms\", &ref);\n" // #3461
|
||||
" sscanf(ip_port, \"%*[^:]:%d\", &port);\n" // #3468
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void foo() {\n"
|
||||
" scanf(\"\", &foo);\n"
|
||||
" scanf(\"%1d\", &foo, &bar);\n"
|
||||
" fscanf(bar, \"%1d\", &foo, &bar);\n"
|
||||
" scanf(\"%*1x %1x %29s\", &count, KeyName, foo);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string has 0 parameters but 1 are given\n"
|
||||
"[test.cpp:3]: (warning) scanf format string has 1 parameters but 2 are given\n"
|
||||
"[test.cpp:4]: (warning) fscanf format string has 1 parameters but 2 are given\n"
|
||||
"[test.cpp:5]: (warning) scanf format string has 2 parameters but 3 are given\n", errout.str());
|
||||
|
||||
check("void foo() {\n"
|
||||
" scanf(\"%1d\");\n"
|
||||
" scanf(\"%1u%1u\", bar());\n"
|
||||
" sscanf(bar, \"%1d%1d\", &foo);\n"
|
||||
" scanf(\"%*1x %1x %29s\", &count);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string has 1 parameters but only 0 are given\n"
|
||||
"[test.cpp:3]: (error) scanf format string has 2 parameters but only 1 are given\n"
|
||||
"[test.cpp:4]: (error) sscanf format string has 2 parameters but only 1 are given\n"
|
||||
"[test.cpp:5]: (error) scanf format string has 2 parameters but only 1 are given\n", errout.str());
|
||||
}
|
||||
|
||||
void testPrintfArgument() {
|
||||
check("void foo() {\n"
|
||||
" printf(\"%u\");\n"
|
||||
" printf(\"%u%s\", 123);\n"
|
||||
" printf(\"%u%s%d\", 0, bar());\n"
|
||||
" printf(\"%u%%%s%d\", 0, bar());\n"
|
||||
" printf(\"%udfd%%dfa%s%d\", 0, bar());\n"
|
||||
" fprintf(stderr,\"%u%s\");\n"
|
||||
" snprintf(str,10,\"%u%s\");\n"
|
||||
" sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364
|
||||
" snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) printf format string has 1 parameters but only 0 are given\n"
|
||||
"[test.cpp:3]: (error) printf format string has 2 parameters but only 1 are given\n"
|
||||
"[test.cpp:4]: (error) printf format string has 3 parameters but only 2 are given\n"
|
||||
"[test.cpp:5]: (error) printf format string has 3 parameters but only 2 are given\n"
|
||||
"[test.cpp:6]: (error) printf format string has 3 parameters but only 2 are given\n"
|
||||
"[test.cpp:7]: (error) fprintf format string has 2 parameters but only 0 are given\n"
|
||||
"[test.cpp:8]: (error) snprintf format string has 2 parameters but only 0 are given\n"
|
||||
"[test.cpp:9]: (error) sprintf format string has 3 parameters but only 2 are given\n"
|
||||
"[test.cpp:10]: (error) snprintf format string has 2 parameters but only 1 are given\n", errout.str());
|
||||
|
||||
check("void foo(char *str) {\n"
|
||||
" printf(\"\", 0);\n"
|
||||
" printf(\"%u\", 123, bar());\n"
|
||||
" printf(\"%u%s\", 0, bar(), 43123);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string has 0 parameters but 1 are given\n"
|
||||
"[test.cpp:3]: (warning) printf format string has 1 parameters but 2 are given\n"
|
||||
"[test.cpp:4]: (warning) printf format string has 2 parameters but 3 are given\n", errout.str());
|
||||
|
||||
check("void foo(char *str) {\n"
|
||||
" printf(\"%u\", 0);\n"
|
||||
" printf(\"%u%s\", 123, bar());\n"
|
||||
" printf(\"%u%s%d\", 0, bar(), 43123);\n"
|
||||
" printf(\"%u%%%s%d\", 0, bar(), 43123);\n"
|
||||
" printf(\"%udfd%%dfa%s%d\", 0, bar(), 43123);\n"
|
||||
" printf(\"%\"PRId64\"\n\", 123);\n"
|
||||
" fprintf(stderr,\"%\"PRId64\"\n\", 123);\n"
|
||||
" snprintf(str,10,\"%\"PRId64\"\n\", 123);\n"
|
||||
" fprintf(stderr, \"error: %m\n\");\n" // #3339
|
||||
" printf(\"string: %.*s\n\", len, string);\n" // #3311
|
||||
" fprintf(stderr, \"%*cText.\n\", indent, ' ');\n" // #3313
|
||||
" sprintf(string1, \"%*\", 32);\n" // #3364
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void foo(char* s, const char* s2, std::string s3, int i) {\n"
|
||||
" printf(\"%s%s\", s, s2);\n"
|
||||
" printf(\"%s\", i);\n"
|
||||
" printf(\"%i%s\", i, i);\n"
|
||||
" printf(\"%s\", s3);\n"
|
||||
" printf(\"%s\", \"s4\");\n"
|
||||
" printf(\"%u\", s);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires a char* given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %s in format string (no. 2) requires a char* given in the argument list\n"
|
||||
"[test.cpp:5]: (warning) %s in format string (no. 1) requires a char* given in the argument list\n", errout.str());
|
||||
|
||||
check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n"
|
||||
" printf(\"%n\", cpi);\n"
|
||||
" printf(\"%n\", ci);\n"
|
||||
" printf(\"%n\", i);\n"
|
||||
" printf(\"%n\", pi);\n"
|
||||
" printf(\"%n\", s);\n"
|
||||
" printf(\"%n\", \"s4\");\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n"
|
||||
"[test.cpp:3]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n"
|
||||
"[test.cpp:6]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n"
|
||||
"[test.cpp:7]: (warning) %n in format string (no. 1) requires a pointer to an non-const integer given in the argument list\n", errout.str());
|
||||
|
||||
check("class foo {};\n"
|
||||
"void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n"
|
||||
" printf(\"%i\", f);\n"
|
||||
" printf(\"%c\", \"s4\");\n"
|
||||
" printf(\"%o\", d);\n"
|
||||
" printf(\"%i\", cpi);\n"
|
||||
" printf(\"%u\", b);\n"
|
||||
" printf(\"%u\", bp);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires an integer given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %c in format string (no. 1) requires an integer given in the argument list\n"
|
||||
"[test.cpp:5]: (warning) %o in format string (no. 1) requires an integer given in the argument list\n", errout.str());
|
||||
|
||||
check("class foo {};\n"
|
||||
"void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n"
|
||||
" printf(\"%p\", f);\n"
|
||||
" printf(\"%p\", c);\n"
|
||||
" printf(\"%p\", bp);\n"
|
||||
" printf(\"%p\", cpi);\n"
|
||||
" printf(\"%p\", b);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %p in format string (no. 1) requires an integer or pointer given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %p in format string (no. 1) requires an integer or pointer given in the argument list\n", errout.str());
|
||||
|
||||
check("class foo {};\n"
|
||||
"void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n"
|
||||
" printf(\"%e\", f);\n"
|
||||
" printf(\"%E\", \"s4\");\n"
|
||||
" printf(\"%f\", cpi);\n"
|
||||
" printf(\"%G\", bp);\n"
|
||||
" printf(\"%f\", d);\n"
|
||||
" printf(\"%f\", b);\n"
|
||||
" printf(\"%f\", (float)cpi);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires a floating point number given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %E in format string (no. 1) requires a floating point number given in the argument list\n"
|
||||
"[test.cpp:5]: (warning) %f in format string (no. 1) requires a floating point number given in the argument list\n"
|
||||
"[test.cpp:6]: (warning) %G in format string (no. 1) requires a floating point number given in the argument list\n", errout.str());
|
||||
|
||||
check("class foo;\n"
|
||||
"void foo(foo f) {\n"
|
||||
" printf(\"%u\", f);\n"
|
||||
" printf(\"%f\", f);\n"
|
||||
" printf(\"%p\", f);\n"
|
||||
"}");
|
||||
TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an integer given in the argument list\n"
|
||||
"[test.cpp:4]: (warning) %f in format string (no. 1) requires an integer given in the argument list\n"
|
||||
"[test.cpp:5]: (warning) %p in format string (no. 1) requires an integer given in the argument list\n", "", errout.str());
|
||||
|
||||
}
|
||||
|
||||
void trac1132() {
|
||||
std::istringstream code("#include <iostream>\n"
|
||||
"class Lock\n"
|
||||
|
@ -5063,45 +4812,6 @@ private:
|
|||
);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void coutCerrMisusage() {
|
||||
check(
|
||||
"void foo() {\n"
|
||||
" std::cout << std::cout;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str());
|
||||
|
||||
check(
|
||||
"void foo() {\n"
|
||||
" std::cout << \"xyz\" << std::cout;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str());
|
||||
|
||||
check(
|
||||
"void foo(int i) {\n"
|
||||
" std::cout << i << std::cerr;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cerr'.\n", errout.str());
|
||||
|
||||
check(
|
||||
"void foo() {\n"
|
||||
" std::cout << \"xyz\";\n"
|
||||
" std::cout << \"xyz\";\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check(
|
||||
"void foo() {\n"
|
||||
" std::cout << std::cout.good();\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check(
|
||||
"void foo() {\n"
|
||||
" MACRO(std::cout <<, << std::cout)\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestOther)
|
||||
|
|
Loading…
Reference in New Issue