Fixed #5104 (False positive: Invalid argument in printf and scanf for vector template)

This commit is contained in:
Robert Reif 2013-10-27 10:48:49 +01:00 committed by Daniel Marjamäki
parent e70f0a601f
commit 94187c41c2
3 changed files with 111 additions and 39 deletions

View File

@ -643,7 +643,8 @@ void CheckIO::checkWrongPrintfScanfArguments()
argInfo.isKnownType() && argInfo.isArrayOrPointer() &&
(!Token::Match(argInfo.typeToken, "char|wchar_t") ||
argInfo.typeToken->strAt(-1) == "const")) {
invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType()))
invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
}
if (scanf_s) {
numSecure++;
@ -669,9 +670,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.typeToken->type() == Token::eString)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
else if (argInfo.isKnownType()) {
if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
if (argInfo.typeToken->isStandardType() || !argInfo.element)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (!argInfo.isArrayOrPointer() ||
argInfo.typeToken->strAt(-1) == "const") {
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
else {
} else {
switch (specifier[0]) {
case 'h':
if (specifier[1] == 'h') {
@ -745,9 +750,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.typeToken->type() == Token::eString)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
else if (argInfo.isKnownType()) {
if (argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
if (argInfo.typeToken->isStandardType() || !argInfo.element)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
} else if (argInfo.typeToken->isUnsigned() ||
!argInfo.isArrayOrPointer() ||
argInfo.typeToken->strAt(-1) == "const") {
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
else {
} else {
switch (specifier[0]) {
case 'h':
if (specifier[1] == 'h') {
@ -812,9 +822,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.typeToken->type() == Token::eString)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
else if (argInfo.isKnownType()) {
if (!argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
if (!Token::Match(argInfo.typeToken, "char|short|int|long")) {
if (argInfo.typeToken->isStandardType() || !argInfo.element)
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (!argInfo.typeToken->isUnsigned() ||
!argInfo.isArrayOrPointer() ||
argInfo.typeToken->strAt(-1) == "const") {
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
else {
} else {
switch (specifier[0]) {
case 'h':
if (specifier[1] == 'h') {
@ -884,9 +899,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.typeToken->type() == Token::eString)
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
else if (argInfo.isKnownType()) {
if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")
if (!Token::Match(argInfo.typeToken, "float|double")) {
if (argInfo.typeToken->isStandardType())
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
} else if (!argInfo.isArrayOrPointer() ||
argInfo.typeToken->strAt(-1) == "const") {
invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo);
else {
} else {
switch (specifier[0]) {
case 'l':
if (specifier[1] == 'l') {
@ -963,8 +982,12 @@ void CheckIO::checkWrongPrintfScanfArguments()
switch (*i) {
case 's':
if (argInfo.variableInfo && argListTok->type() != Token::eString &&
argInfo.isKnownType() && !argInfo.isArrayOrPointer())
invalidPrintfArgTypeError_s(tok, numFormat);
argInfo.isKnownType() && !argInfo.isArrayOrPointer()) {
if (!Token::Match(argInfo.typeToken, "char|wchar_t")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_s(tok, numFormat, &argInfo);
}
}
done = true;
break;
case 'n':
@ -983,9 +1006,10 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.isArrayOrPointer() && !argInfo.element) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
} else if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t"))
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
else {
} else if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_int(tok, numFormat, specifier, &argInfo);
} else {
switch (specifier[0]) {
case 'l':
if (specifier[1] == 'l') {
@ -1039,9 +1063,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.isArrayOrPointer() && !argInfo.element) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
} else if ((argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "bool|short|long|int")) && !Token::Match(argInfo.typeToken, "char|short"))
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
else {
} else if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
} else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo);
} else {
switch (specifier[0]) {
case 'l':
if (specifier[1] == 'l') {
@ -1097,9 +1125,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.isArrayOrPointer() && !argInfo.element) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
} else if ((!argInfo.typeToken->isUnsigned() || !Token::Match(argInfo.typeToken, "char|short|long|int")) && argInfo.typeToken->str() != "bool")
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
else {
} else if (!argInfo.typeToken->isUnsigned() && argInfo.typeToken->str() != "bool") {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
} else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo);
} else {
switch (specifier[0]) {
case 'l':
if (specifier[1] == 'l') {
@ -1165,12 +1197,12 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argInfo.isArrayOrPointer() && !argInfo.element) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
} else if (!Token::Match(argInfo.typeToken, "float|double"))
} else if (!Token::Match(argInfo.typeToken, "float|double")) {
if (!(!argInfo.isArrayOrPointer() && argInfo.element))
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
} else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
(specifier[0] != 'L' && argInfo.typeToken->isLong()))
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) ||
(specifier[0] != 'L' && argInfo.typeToken->isLong()))
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
} else if (argInfo.isArrayOrPointer() && !argInfo.element) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo);
@ -1600,10 +1632,12 @@ void CheckIO::invalidScanfArgTypeError_float(const Token* tok, unsigned int numF
reportError(tok, Severity::warning, "invalidScanfArgType_float", errmsg.str());
}
void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat)
void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo)
{
std::ostringstream errmsg;
errmsg << "%s in format string (no. " << numFormat << ") requires a char* given in the argument list.";
errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is ";
argumentType(errmsg, argInfo);
errmsg << ".";
reportError(tok, Severity::warning, "invalidPrintfArgType_s", errmsg.str());
}
void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo)
@ -1716,11 +1750,17 @@ void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo)
os << type->str() << " ";
type = type->next();
}
while (Token::Match(type, "%any% ::")) {
os << type->str() << "::";
type = type->tokAt(2);
}
type->stringify(os, false, true);
if (type->strAt(1) == "*" && !argInfo->element)
os << " *";
else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray())
os << " *";
else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray())
os << " *";
if (argInfo->address)
os << " *";
} else {

View File

@ -111,7 +111,7 @@ private:
void invalidScanfArgTypeError_s(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
void invalidScanfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned);
void invalidScanfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat, const ArgumentInfo* argInfo);
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier, const ArgumentInfo* argInfo);
@ -136,7 +136,7 @@ private:
c.invalidScanfArgTypeError_s(0, 1, "s", NULL);
c.invalidScanfArgTypeError_int(0, 1, "d", NULL, false);
c.invalidScanfArgTypeError_float(0, 1, "f", NULL);
c.invalidPrintfArgTypeError_s(0, 1);
c.invalidPrintfArgTypeError_s(0, 1, NULL);
c.invalidPrintfArgTypeError_n(0, 1, NULL);
c.invalidPrintfArgTypeError_p(0, 1, NULL);
c.invalidPrintfArgTypeError_int(0, 1, "X", NULL);

View File

@ -1305,6 +1305,20 @@ private:
"[test.cpp:23]: (warning) %n in format string (no. 23) requires 'int *' but the argument type is 'const wchar_t *'.\n"
"[test.cpp:23]: (warning) %n in format string (no. 24) requires 'int *' but the argument type is 'const int *'.\n", errout.str());
check("void g() {\n" // #5104
" myvector<int> v1(1);\n"
" scanf(\"%d\n\",&v1[0]);\n"
" myvector<unsigned int> v2(1);\n"
" scanf(\"%u\n\",&v2[0]);\n"
" myvector<unsigned int> v3(1);\n"
" scanf(\"%x\n\",&v3[0]);\n"
" myvector<double> v4(1);\n"
" scanf(\"%lf\n\",&v4[0]);\n"
" myvector<char *> v5(1);\n"
" scanf(\"%10s\n\",v5[0]);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void testPrintfArgument() {
@ -1371,9 +1385,9 @@ private:
" 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"
ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n"
"[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str());
check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n"
@ -1387,7 +1401,7 @@ private:
ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int *'.\n"
"[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const int'.\n"
"[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'int'.\n"
"[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std'.\n"
"[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std::string'.\n"
"[test.cpp:7]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout.str());
check("class foo {};\n"
@ -2059,6 +2073,24 @@ private:
"[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'long'.\n"
"[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'long'.\n", errout.str());
check("void f() {\n" // #5104
" myvector<unsigned short> v1(1,0);\n"
" printf(\"%d\n\",v1[0]);\n"
" myvector<int> v2(1,0);\n"
" printf(\"%d\n\",v2[0]);\n"
" myvector<unsigned int> v3(1,0);\n"
" printf(\"%u\n\",v3[0]);\n"
" myvector<unsigned int> v4(1,0);\n"
" printf(\"%x\n\",v4[0]);\n"
" myvector<double> v5(1,0);\n"
" printf(\"%f\n\",v5[0]);\n"
" myvector<bool> v6(1,0);\n"
" printf(\"%u\n\",v6[0]);\n"
" myvector<char *> v7(1,0);\n"
" printf(\"%s\n\",v7[0]);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
@ -2441,17 +2473,17 @@ private:
" printf(format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n"
" printf(format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:6]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n"
"[test.cpp:7]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
"[test.cpp:7]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:7]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n"
"[test.cpp:9]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
"[test.cpp:9]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:9]: (warning) sprintf format string requires 5 parameters but 6 are given.\n"
"[test.cpp:10]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
"[test.cpp:10]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:10]: (warning) sprintf format string requires 5 parameters but 6 are given.\n"
"[test.cpp:12]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
"[test.cpp:12]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:12]: (warning) printf format string requires 5 parameters but 6 are given.\n"
"[test.cpp:13]: (warning) %s in format string (no. 5) requires a char* given in the argument list.\n"
"[test.cpp:13]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'int'.\n"
"[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n", errout.str());
}