From cc6745fef62d1018561f2402ab8bc71d058f1fdb Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Tue, 1 Oct 2013 05:49:44 +0200 Subject: [PATCH] CheckIO: Fixed false positives when using _snprintf_s and _snwprintf_s. Ticket: #5057 --- lib/checkio.cpp | 32 ++++++++++++- lib/tokenize.cpp | 7 ++- test/testio.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 149 insertions(+), 12 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index ac7219276..d919c1bcd 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -475,7 +475,7 @@ void CheckIO::checkWrongPrintfScanfArguments() } else { continue; } - } else if (windows && Token::Match(tok, "snprintf_s|snwprintf_s (")) { + } 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) @@ -513,17 +513,25 @@ void CheckIO::checkWrongPrintfScanfArguments() } ++i; } + if (scanf_s) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } if (i == formatString.end()) break; } else if (percent) { percent = false; bool _continue = false; + bool skip = false; std::string width; unsigned int parameterPosition = 0; bool hasParameterPosition = false; - while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { + while (i != formatString.end() && *i != '[' && !std::isalpha(*i)) { if (*i == '*') { + skip = true; if (scan) _continue = true; else { @@ -540,6 +548,26 @@ void CheckIO::checkWrongPrintfScanfArguments() } ++i; } + if (*i == '[') { + while (i != formatString.end()) { + if (*i == ']') { + if (!skip) { + numFormat++; + if (argListTok) + argListTok = argListTok->nextArgument(); + } + break; + } + ++i; + } + if (scanf_s && !skip) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } + _continue = true; + } if (i == formatString.end()) break; if (_continue) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 729128096..9ad16a87a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5587,6 +5587,9 @@ void Tokenizer::simplifyPlatformTypes() tok->str("const"); tok->insertToken("*"); tok->insertToken("wchar_t"); + } else if (tok->str() == "WCHAR") { + tok->originalName(tok->str()); + tok->str("wchar_t"); } } } @@ -9612,7 +9615,7 @@ void Tokenizer::simplifyMicrosoftStringFunctions() tok->str("sprintf_s"); tok->originalName("_stprintf_s"); } else if (Token::simpleMatch(tok, "_sntprintf_s (")) { - tok->str("snprintf_s"); + tok->str("_snprintf_s"); tok->originalName("_sntprintf_s"); } else if (Token::simpleMatch(tok, "_tscanf_s (")) { tok->str("scanf_s"); @@ -9682,7 +9685,7 @@ void Tokenizer::simplifyMicrosoftStringFunctions() tok->str("swprintf_s"); tok->originalName("_stprintf_s"); } else if (Token::simpleMatch(tok, "_sntprintf_s (")) { - tok->str("snwprintf_s"); + tok->str("_snwprintf_s"); tok->originalName("_sntprintf_s"); } else if (Token::simpleMatch(tok, "_tscanf_s (")) { tok->str("wscanf_s"); diff --git a/test/testio.cpp b/test/testio.cpp index 859026c2c..c15342875 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -2185,6 +2185,24 @@ private: "[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" + " printf_s(\"%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) printf_s format string requires 2 parameters but 3 are given.\n", errout.str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " wprintf_s(L\"%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) wprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); + check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" @@ -2205,6 +2223,26 @@ private: "[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" + " char str[10];\n" + " int i;\n" + " unsigned int u;\n" + " sprintf_s(str, sizeof(str), \"%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) sprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); + + check("void foo() {\n" + " wchar_t str[10];\n" + " int i;\n" + " unsigned int u;\n" + " swprintf_s(str, sizeof(str) / sizeof(wchar_t), L\"%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) swprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); + check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" @@ -2224,6 +2262,26 @@ private: 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" + " char str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _snprintf_s(str, sizeof(str), _TRUNCATE, \"%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) _snprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); + + check("void foo() {\n" + " wchar_t str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _snwprintf_s(str, sizeof(str) / sizeof(wchar_t), _TRUNCATE, L\"%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) _snwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); } void testMicrosoftSecureScanfArgument() { @@ -2231,43 +2289,91 @@ private: " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" - " _tscanf_s(\"%s %d %u\", str, 10, &u, &i, 0)\n" + " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 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()); + "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 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" + " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 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()); + "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " char str[10];\n" + " scanf_s(\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 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) scanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " wchar_t str[10];\n" + " wscanf_s(L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 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) wscanf_s format string requires 6 parameters but 7 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" + " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 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()); + "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 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" + " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 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()); + "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); + + check("void foo() {\n" + " char txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " char str[10];\n" + " sscanf_s(txt, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 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) sscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); + + check("void foo() {\n" + " wchar_t txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " wchar_t str[10];\n" + " swscanf_s(txt, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 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) swscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); + + check("void foo() {\n" + " WCHAR msStr1[5] = {0};\n" + " wscanf_s(L\"%4[^-]\", msStr1, _countof(msStr1));\n" + "}\n", false, false, Settings::Win32W); + ASSERT_EQUALS("", errout.str()); } void testlibrarycfg() {