From 94187c41c2cc2564b6ce57544113115276568750 Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Sun, 27 Oct 2013 10:48:49 +0100 Subject: [PATCH] Fixed #5104 (False positive: Invalid argument in printf and scanf for vector template) --- lib/checkio.cpp | 94 +++++++++++++++++++++++++++++++++++-------------- lib/checkio.h | 4 +-- test/testio.cpp | 52 +++++++++++++++++++++------ 3 files changed, 111 insertions(+), 39 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index a9333d857..d2680cac4 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -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 { diff --git a/lib/checkio.h b/lib/checkio.h index fde9541a7..dab30a900 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -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); diff --git a/test/testio.cpp b/test/testio.cpp index 199c3f1e1..13eac2374 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -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 v1(1);\n" + " scanf(\"%d\n\",&v1[0]);\n" + " myvector v2(1);\n" + " scanf(\"%u\n\",&v2[0]);\n" + " myvector v3(1);\n" + " scanf(\"%x\n\",&v3[0]);\n" + " myvector v4(1);\n" + " scanf(\"%lf\n\",&v4[0]);\n" + " myvector 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 v1(1,0);\n" + " printf(\"%d\n\",v1[0]);\n" + " myvector v2(1,0);\n" + " printf(\"%d\n\",v2[0]);\n" + " myvector v3(1,0);\n" + " printf(\"%u\n\",v3[0]);\n" + " myvector v4(1,0);\n" + " printf(\"%x\n\",v4[0]);\n" + " myvector v5(1,0);\n" + " printf(\"%f\n\",v5[0]);\n" + " myvector v6(1,0);\n" + " printf(\"%u\n\",v6[0]);\n" + " myvector 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()); }