Improved checking of scanf format strings
This commit is contained in:
parent
3db58bb57f
commit
dc4982115a
|
@ -25,6 +25,7 @@
|
||||||
#include "symboldatabase.h"
|
#include "symboldatabase.h"
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -443,6 +444,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
percent = false;
|
percent = false;
|
||||||
|
|
||||||
bool _continue = false;
|
bool _continue = false;
|
||||||
|
std::string width;
|
||||||
while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
|
while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
|
||||||
if (*i == '*') {
|
if (*i == '*') {
|
||||||
if (scan)
|
if (scan)
|
||||||
|
@ -452,7 +454,8 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
if (argListTok)
|
if (argListTok)
|
||||||
argListTok = argListTok->nextArgument();
|
argListTok = argListTok->nextArgument();
|
||||||
}
|
}
|
||||||
}
|
} else if (std::isdigit(*i))
|
||||||
|
width += *i;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
if (i == formatString.end())
|
if (i == formatString.end())
|
||||||
|
@ -473,6 +476,16 @@ void CheckIO::checkWrongPrintfScanfArguments()
|
||||||
if (scan && varTypeTok) {
|
if (scan && varTypeTok) {
|
||||||
if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")
|
if ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")
|
||||||
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
invalidScanfArgTypeError(tok, tok->str(), numFormat);
|
||||||
|
|
||||||
|
if (*i == 's' && variableInfo && isKnownType(variableInfo, varTypeTok) && variableInfo->isArray() && (variableInfo->dimensions().size() == 1)) {
|
||||||
|
if (width.empty())
|
||||||
|
missingScanfFormatWidthError(tok, tok->str(), numFormat, variableInfo);
|
||||||
|
else {
|
||||||
|
int numWidth = std::atoi(width.c_str());
|
||||||
|
if (numWidth != (variableInfo->dimension(0) - 1))
|
||||||
|
invalidScanfFormatWidthError(tok, tok->str(), numFormat, numWidth, variableInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (!scan) {
|
} else if (!scan) {
|
||||||
switch (*i) {
|
switch (*i) {
|
||||||
case 's':
|
case 's':
|
||||||
|
@ -599,3 +612,35 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int num
|
||||||
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list";
|
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());
|
reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
|
||||||
}
|
}
|
||||||
|
void CheckIO::missingScanfFormatWidthError(const Token* tok, const std::string &functionName, unsigned int numFormat, const Variable *var)
|
||||||
|
{
|
||||||
|
std::ostringstream errmsg;
|
||||||
|
errmsg << functionName << " %s in format string (no. " << numFormat << ") does not specify a width";
|
||||||
|
if (var)
|
||||||
|
errmsg << ", use %" << (var->dimension(0) - 1) << "s to prevent overflowing destination: " << var->name() << "[" << var->dimension(0) << "]";
|
||||||
|
reportError(tok, Severity::warning, "missingScanfFormatWidth", errmsg.str());
|
||||||
|
}
|
||||||
|
void CheckIO::invalidScanfFormatWidthError(const Token* tok, const std::string &functionName, unsigned int numFormat, int width, const Variable *var)
|
||||||
|
{
|
||||||
|
std::ostringstream errmsg;
|
||||||
|
Severity::SeverityType severity = Severity::warning;
|
||||||
|
bool inconclusive = false;
|
||||||
|
|
||||||
|
if (var) {
|
||||||
|
if (var->dimension(0) > width) {
|
||||||
|
if (!_settings->inconclusive)
|
||||||
|
return;
|
||||||
|
inconclusive = true;
|
||||||
|
errmsg << functionName << " width " << width << " in format string (no. " << numFormat << ") is smaller than destination"
|
||||||
|
<< ": " << var->name() << "[" << var->dimension(0) << "]";
|
||||||
|
} else {
|
||||||
|
errmsg << functionName << " width " << width << " in format string (no. " << numFormat << ") is larger than destination"
|
||||||
|
<< ", use %" << (var->dimension(0) - 1) << "s to prevent overflowing destination: " << var->name() << "[" << var->dimension(0) << "]";
|
||||||
|
severity = Severity::error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
errmsg << functionName << " width " << width << " in format string (no. " << numFormat << ") doesn't match destination";
|
||||||
|
|
||||||
|
reportError(tok, severity, "invalidScanfFormatWidth", errmsg.str(), inconclusive);
|
||||||
|
}
|
||||||
|
|
|
@ -24,10 +24,11 @@
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
class Variable;
|
||||||
|
|
||||||
/// @addtogroup Checks
|
/// @addtogroup Checks
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
|
||||||
/** @brief %Check input output operations. */
|
/** @brief %Check input output operations. */
|
||||||
class CPPCHECKLIB CheckIO : public Check {
|
class CPPCHECKLIB CheckIO : public Check {
|
||||||
public:
|
public:
|
||||||
|
@ -87,6 +88,8 @@ private:
|
||||||
void invalidPrintfArgTypeError_p(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_int(const Token* tok, unsigned int numFormat, char c);
|
||||||
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
|
||||||
|
void missingScanfFormatWidthError(const Token* tok, const std::string &functionName, unsigned int numFormat, const Variable *var);
|
||||||
|
void invalidScanfFormatWidthError(const Token* tok, const std::string &functionName, unsigned int numFormat, int width, const Variable *var);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
CheckIO c(0, settings, errorLogger);
|
CheckIO c(0, settings, errorLogger);
|
||||||
|
@ -105,6 +108,8 @@ private:
|
||||||
c.invalidPrintfArgTypeError_p(0, 1);
|
c.invalidPrintfArgTypeError_p(0, 1);
|
||||||
c.invalidPrintfArgTypeError_int(0, 1, 'u');
|
c.invalidPrintfArgTypeError_int(0, 1, 'u');
|
||||||
c.invalidPrintfArgTypeError_float(0, 1, 'f');
|
c.invalidPrintfArgTypeError_float(0, 1, 'f');
|
||||||
|
c.missingScanfFormatWidthError(0, "scanf", 1, NULL);
|
||||||
|
c.invalidScanfFormatWidthError(0, "scanf", 10, 5, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string myName() const {
|
std::string myName() const {
|
||||||
|
|
|
@ -48,12 +48,13 @@ private:
|
||||||
TEST_CASE(testPrintfArgument);
|
TEST_CASE(testPrintfArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[]) {
|
void check(const char code[], bool inconclusive = false) {
|
||||||
// Clear the error buffer..
|
// Clear the error buffer..
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.addEnabled("style");
|
settings.addEnabled("style");
|
||||||
|
settings.inconclusive = inconclusive;
|
||||||
|
|
||||||
// Tokenize..
|
// Tokenize..
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
@ -443,6 +444,31 @@ private:
|
||||||
"[test.cpp:3]: (error) scanf format string has 2 parameters but only 1 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: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());
|
"[test.cpp:5]: (error) scanf format string has 2 parameters but only 1 are given\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" char input[10];\n"
|
||||||
|
" char output[5];\n"
|
||||||
|
" sscanf(input, \"%s\", output);\n"
|
||||||
|
" sscanf(input, \"%3s\", output);\n"
|
||||||
|
" sscanf(input, \"%4s\", output);\n"
|
||||||
|
" sscanf(input, \"%5s\", output);\n"
|
||||||
|
"}", false);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) sscanf %s in format string (no. 1) does not specify a width, use %4s to prevent overflowing destination: output[5]\n"
|
||||||
|
"[test.cpp:7]: (error) sscanf width 5 in format string (no. 1) is larger than destination, use %4s to prevent overflowing destination: output[5]\n"
|
||||||
|
"[test.cpp:4]: (warning) scanf without field width limits can crash with huge input data\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" char input[10];\n"
|
||||||
|
" char output[5];\n"
|
||||||
|
" sscanf(input, \"%s\", output);\n"
|
||||||
|
" sscanf(input, \"%3s\", output);\n"
|
||||||
|
" sscanf(input, \"%4s\", output);\n"
|
||||||
|
" sscanf(input, \"%5s\", output);\n"
|
||||||
|
"}", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) sscanf %s in format string (no. 1) does not specify a width, use %4s to prevent overflowing destination: output[5]\n"
|
||||||
|
"[test.cpp:5]: (warning, inconclusive) sscanf width 3 in format string (no. 1) is smaller than destination: output[5]\n"
|
||||||
|
"[test.cpp:7]: (error) sscanf width 5 in format string (no. 1) is larger than destination, use %4s to prevent overflowing destination: output[5]\n"
|
||||||
|
"[test.cpp:4]: (warning) scanf without field width limits can crash with huge input data\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testPrintfArgument() {
|
void testPrintfArgument() {
|
||||||
|
|
Loading…
Reference in New Issue