Fixed #5057 (Microsoft secure printf/scanf support.)

This commit is contained in:
Robert Reif 2013-09-30 19:55:21 +02:00 committed by Daniel Marjamäki
parent e3b7bce72b
commit c37c6617d3
3 changed files with 217 additions and 36 deletions

View File

@ -439,9 +439,10 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (!formatString.empty()) {
/* formatstring found in library */
} else if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%") ||
(windows && (Token::Match(tok, "Format|AppendFormat ( %str%") &&
Token::Match(tok->tokAt(-2), "%var% .") && tok->tokAt(-2)->variable() &&
tok->tokAt(-2)->variable()->typeStartToken()->str() == "CString"))) {
(windows && (Token::Match(tok, "printf_s|wprintf_s|scanf_s|wscanf_s ( %str%") ||
(Token::Match(tok, "Format|AppendFormat ( %str%") &&
Token::Match(tok->tokAt(-2), "%var% .") && tok->tokAt(-2)->variable() &&
tok->tokAt(-2)->variable()->typeStartToken()->str() == "CString")))) {
formatString = tok->strAt(2);
if (tok->strAt(3) == ",") {
@ -451,7 +452,9 @@ void CheckIO::checkWrongPrintfScanfArguments()
} else {
continue;
}
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%") || (Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%"))) {
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%") ||
(Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) ||
(windows && Token::Match(tok, "sscanf_s|swscanf_s ( %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)
@ -459,7 +462,9 @@ void CheckIO::checkWrongPrintfScanfArguments()
} else {
continue;
}
} else if (Token::Match(tok, "snprintf|fnprintf (") || (Token::simpleMatch(tok, "swprintf (") && !Token::Match(tok->tokAt(2)->nextArgument(), "%str%"))) {
} else if (Token::Match(tok, "snprintf|fnprintf (") ||
(Token::simpleMatch(tok, "swprintf (") && !Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) ||
(windows && Token::Match(tok, "sprintf_s|swprintf_s ("))) {
const Token* formatStringTok = tok->tokAt(2);
for (int i = 0; i < 2 && formatStringTok; i++) {
formatStringTok = formatStringTok->nextArgument(); // Find third parameter (format string)
@ -470,13 +475,27 @@ void CheckIO::checkWrongPrintfScanfArguments()
} else {
continue;
}
} else if (windows && Token::Match(tok, "snprintf_s|snwprintf_s (")) {
const Token* formatStringTok = tok->tokAt(2);
for (int i = 0; i < 3 && formatStringTok; i++) {
formatStringTok = formatStringTok->nextArgument(); // Find forth parameter (format string)
}
if (Token::Match(formatStringTok, "%str% [,)]")) {
argListTok = formatStringTok->nextArgument(); // 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|swscanf|fwscanf|wscanf");
bool scanf_s = windows ? Token::Match(tok, "scanf_s|wscanf_s|sscanf_s|swscanf_s") : false;
bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf") || scanf_s;
unsigned int numFormat = 0;
unsigned int numSecure = 0;
bool percent = false;
const Token* argListTok2 = argListTok;
std::set<unsigned int> parameterPositionsUsed;
@ -561,6 +580,21 @@ void CheckIO::checkWrongPrintfScanfArguments()
argInfo.typeToken->strAt(-1) == "const")) {
invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo);
}
if (scanf_s) {
numSecure++;
if (argListTok) {
argListTok = argListTok->nextArgument();
}
}
done = true;
break;
case 'c':
if (scanf_s) {
numSecure++;
if (argListTok) {
argListTok = argListTok->nextArgument();
}
}
done = true;
break;
case 'x':
@ -1134,8 +1168,8 @@ void CheckIO::checkWrongPrintfScanfArguments()
}
// Mismatching number of parameters => warning
if (numFormat != numFunction)
wrongPrintfScanfArgumentsError(tok, tok->str(), numFormat, numFunction);
if ((numFormat + numSecure) != numFunction)
wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction);
}
}
}
@ -1374,12 +1408,13 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
std::ostringstream errmsg;
errmsg << functionName
<< " format string has "
<< " format string requires "
<< numFormat
<< " parameters but "
<< " parameter" << (numFormat != 1 ? "s" : "") << " but "
<< (numFormat > numFunction ? "only " : "")
<< numFunction
<< " are given.";
<< (numFunction != 1 ? " are" : " is")
<< " given.";
reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str());
}

View File

@ -9562,14 +9562,34 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
tok->str("strtok");
} else if (Token::simpleMatch(tok, "_tprintf (")) {
tok->str("printf");
tok->originalName("_tprintf");
} else if (Token::simpleMatch(tok, "_stprintf (")) {
tok->str("sprintf");
tok->originalName("_stprintf");
} else if (Token::simpleMatch(tok, "_sntprintf (")) {
tok->str("snprintf");
tok->originalName("_sntprintf");
} else if (Token::simpleMatch(tok, "_tscanf (")) {
tok->str("scanf");
tok->originalName("_tscanf");
} else if (Token::simpleMatch(tok, "_stscanf (")) {
tok->str("sscanf");
tok->originalName("_stscanf");
} else if (Token::simpleMatch(tok, "_tprintf_s (")) {
tok->str("printf_s");
tok->originalName("_tprintf_s");
} else if (Token::simpleMatch(tok, "_stprintf_s (")) {
tok->str("sprintf_s");
tok->originalName("_stprintf_s");
} else if (Token::simpleMatch(tok, "_sntprintf_s (")) {
tok->str("snprintf_s");
tok->originalName("_sntprintf_s");
} else if (Token::simpleMatch(tok, "_tscanf_s (")) {
tok->str("scanf_s");
tok->originalName("_tscanf_s");
} else if (Token::simpleMatch(tok, "_stscanf_s (")) {
tok->str("sscanf_s");
tok->originalName("_stscanf_s");
} else if (Token::Match(tok, "_T ( %char%|%str% )")) {
tok->deleteNext();
tok->deleteThis();
@ -9612,14 +9632,34 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
tok->str("wcstok");
} else if (Token::simpleMatch(tok, "_tprintf (")) {
tok->str("wprintf");
tok->originalName("_tprintf");
} else if (Token::simpleMatch(tok, "_stprintf (")) {
tok->str("swprintf");
tok->originalName("_stprintf");
} else if (Token::simpleMatch(tok, "_sntprintf (")) {
tok->str("snwprintf");
tok->originalName("_sntprintf");
} else if (Token::simpleMatch(tok, "_tscanf (")) {
tok->str("wscanf");
tok->originalName("_tscanf");
} else if (Token::simpleMatch(tok, "_stscanf (")) {
tok->str("swscanf");
tok->originalName("_stscanf");
} else if (Token::simpleMatch(tok, "_tprintf_s (")) {
tok->str("wprintf_s");
tok->originalName("_tprintf_s");
} else if (Token::simpleMatch(tok, "_stprintf_s (")) {
tok->str("swprintf_s");
tok->originalName("_stprintf_s");
} else if (Token::simpleMatch(tok, "_sntprintf_s (")) {
tok->str("snwprintf_s");
tok->originalName("_sntprintf_s");
} else if (Token::simpleMatch(tok, "_tscanf_s (")) {
tok->str("wscanf_s");
tok->originalName("_tscanf_s");
} else if (Token::simpleMatch(tok, "_stscanf_s (")) {
tok->str("swscanf_s");
tok->originalName("_stscanf_s");
} else if (Token::Match(tok, "_T ( %char%|%str% )")) {
tok->deleteNext();
tok->deleteThis();

View File

@ -50,6 +50,8 @@ private:
TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902
TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920
TEST_CASE(testMicrosoftSecurePrintfArgument);
TEST_CASE(testMicrosoftSecureScanfArgument);
TEST_CASE(testlibrarycfg); // library configuration
}
@ -460,7 +462,7 @@ private:
" scanf(\"aa%ld\", &a);\n" // No %s
" scanf(\"%*[^~]\");\n" // Ignore input
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string has 0 parameters but 1 are given.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string requires 0 parameters but 1 is given.\n", errout.str());
}
void testScanf3() {
@ -507,10 +509,10 @@ private:
" 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());
ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string requires 0 parameters but 1 is given.\n"
"[test.cpp:3]: (warning) scanf format string requires 1 parameter but 2 are given.\n"
"[test.cpp:4]: (warning) fscanf format string requires 1 parameter but 2 are given.\n"
"[test.cpp:5]: (warning) scanf format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" scanf(\"%1d\");\n"
@ -518,10 +520,10 @@ private:
" 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());
ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string requires 1 parameter but only 0 are given.\n"
"[test.cpp:3]: (error) scanf format string requires 2 parameters but only 1 is given.\n"
"[test.cpp:4]: (error) sscanf format string requires 2 parameters but only 1 is given.\n"
"[test.cpp:5]: (error) scanf format string requires 2 parameters but only 1 is given.\n", errout.str());
check("void foo() {\n"
" char input[10];\n"
@ -1316,24 +1318,24 @@ private:
" 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());
ASSERT_EQUALS("[test.cpp:2]: (error) printf format string requires 1 parameter but only 0 are given.\n"
"[test.cpp:3]: (error) printf format string requires 2 parameters but only 1 is given.\n"
"[test.cpp:4]: (error) printf format string requires 3 parameters but only 2 are given.\n"
"[test.cpp:5]: (error) printf format string requires 3 parameters but only 2 are given.\n"
"[test.cpp:6]: (error) printf format string requires 3 parameters but only 2 are given.\n"
"[test.cpp:7]: (error) fprintf format string requires 2 parameters but only 0 are given.\n"
"[test.cpp:8]: (error) snprintf format string requires 2 parameters but only 0 are given.\n"
"[test.cpp:9]: (error) sprintf format string requires 3 parameters but only 2 are given.\n"
"[test.cpp:10]: (error) snprintf format string requires 2 parameters but only 1 is 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());
ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string requires 0 parameters but 1 is given.\n"
"[test.cpp:3]: (warning) printf format string requires 1 parameter but 2 are given.\n"
"[test.cpp:4]: (warning) printf format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n" // swprintf exists as MSVC extension and as standard function: #4790
" swprintf(string1, L\"%u\", 32, string2);\n" // MSVC implementation
@ -1341,8 +1343,8 @@ private:
" swprintf(string1, 6, L\"%u\", 32, string2);\n" // Standard implementation
" swprintf(string1, 6, L\"%u%s\", 32, string2);\n" // Standard implementation
"}");
ASSERT_EQUALS("[test.cpp:2]: (warning) swprintf format string has 1 parameters but 2 are given.\n"
"[test.cpp:4]: (warning) swprintf format string has 1 parameters but 2 are given.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (warning) swprintf format string requires 1 parameter but 2 are given.\n"
"[test.cpp:4]: (warning) swprintf format string requires 1 parameter but 2 are given.\n", errout.str());
check("void foo(char *str) {\n"
" printf(\"%u\", 0);\n"
@ -2052,7 +2054,7 @@ private:
" scanf(\"%2$d\", &bar);\n"
" printf(\"%0$f\", 0.0);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) printf format string has 1 parameters but only 0 are given.\n"
ASSERT_EQUALS("[test.cpp:3]: (error) printf format string requires 1 parameter but only 0 are given.\n"
"[test.cpp:4]: (warning) printf: referencing parameter 4 while 3 arguments given\n"
"[test.cpp:5]: (warning) scanf: referencing parameter 2 while 1 arguments given\n"
"[test.cpp:6]: (warning) printf: parameter positions start at 1, not 0\n"
@ -2164,6 +2166,110 @@ private:
"[test.cpp:5]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n", errout.str());
}
void testMicrosoftSecurePrintfArgument() {
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
" _tprintf_s(_T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
" _tprintf_s(_T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR str[10];\n"
" int i;\n"
" unsigned int u;\n"
" _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR str[10];\n"
" int i;\n"
" unsigned int u;\n"
" _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR str[10];\n"
" int i;\n"
" unsigned int u;\n"
" _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR str[10];\n"
" int i;\n"
" unsigned int u;\n"
" _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
}
void testMicrosoftSecureScanfArgument() {
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _tscanf_s(\"%s %d %u\", str, 10, &u, &i, 0)\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) _tscanf_s format string requires 4 parameters but 5 are given.\n", errout.str());
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _tscanf_s(\"%s %d %u\", str, 10, &u, &i, 0)\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) _tscanf_s format string requires 4 parameters but 5 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR txt[100];\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _stscanf_s(txt, \"%s %d %u\", str, 10, &u, &i, 0)\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:6]: (warning) _stscanf_s format string requires 4 parameters but 5 are given.\n", errout.str());
check("void foo() {\n"
" TCHAR txt[100];\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _stscanf_s(txt, \"%s %d %u\", str, 10, &u, &i, 0)\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:6]: (warning) _stscanf_s format string requires 4 parameters but 5 are given.\n", errout.str());
}
void testlibrarycfg() {
const char code[] = "void f() {\n"
" format(\"%s\");\n"
@ -2177,7 +2283,7 @@ private:
Library lib;
lib.argumentChecks["format"][1].formatstr = true;
check(code, false, false, Settings::Unspecified, &lib);
ASSERT_EQUALS("[test.cpp:2]: (error) format format string has 1 parameters but only 0 are given.\n", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (error) format format string requires 1 parameter but only 0 are given.\n", errout.str());
}
};