diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 0a0ca70a833..f726cef0cc7 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -666,16 +666,20 @@ void CheckIOImpl::wrongfeofUsage(const Token * tok) // printf("", 1); // Too much arguments //--------------------------------------------------------------------------- -static bool findFormat(nonneg int arg, const Token *firstArg, +static bool findFormat(nonneg int arg, nonneg int argc, const Token *firstArg, const Token *&formatStringTok, const Token *&formatArgTok) { + formatArgTok = firstArg; + + for (int i = 0; i < argc && formatArgTok; ++i) + formatArgTok = formatArgTok->nextArgument(); + const Token* argTok = firstArg; for (int i = 0; i < arg && argTok; ++i) argTok = argTok->nextArgument(); if (Token::Match(argTok, "%str% [,)]")) { - formatArgTok = argTok->nextArgument(); formatStringTok = argTok; return true; } @@ -686,7 +690,6 @@ static bool findFormat(nonneg int arg, const Token *firstArg, (argTok->variable()->dimensions().size() == 1 && argTok->variable()->dimensionKnown(0) && argTok->variable()->dimension(0) != 0))) { - formatArgTok = argTok->nextArgument(); if (!argTok->values().empty()) { const auto value = std::find_if( argTok->values().cbegin(), argTok->values().cend(), std::mem_fn(&ValueFlow::Value::isTokValue)); @@ -706,6 +709,17 @@ static inline bool typesMatch(const std::string& iToTest, const std::string& iTy return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename); } +static int getMaxArgNo(const Library::Function *func) +{ + const auto &checks = func->argumentChecks; + return std::max_element(checks.cbegin(), checks.cend(), + [](const std::pair &lhs, + const std::pair &rhs) { + return lhs.first < rhs.first; + } + )->first; +} + void CheckIOImpl::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -723,8 +737,10 @@ void CheckIOImpl::checkWrongPrintfScanfArguments() bool scan = false; bool scanf_s = false; int formatStringArgNo = -1; + int argc = -1; if (tok->strAt(1) == "(" && mSettings.library.formatstr_function(tok)) { + argc = getMaxArgNo(mSettings.library.getFunction(tok)); formatStringArgNo = mSettings.library.formatstr_argno(tok); scan = mSettings.library.formatstr_scan(tok); scanf_s = mSettings.library.formatstr_secure(tok); @@ -732,37 +748,37 @@ void CheckIOImpl::checkWrongPrintfScanfArguments() if (formatStringArgNo >= 0) { // formatstring found in library. Find format string and first argument belonging to format string. - if (!findFormat(formatStringArgNo, tok->tokAt(2), formatStringTok, argListTok)) + if (!findFormat(formatStringArgNo, argc, tok->tokAt(2), formatStringTok, argListTok)) continue; } else if (Token::simpleMatch(tok, "swprintf (")) { if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { // Find third parameter and format string - if (!findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) + if (!findFormat(1, 2, tok->tokAt(2), formatStringTok, argListTok)) continue; } else { // Find fourth parameter and format string - if (!findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) + if (!findFormat(2, 3, tok->tokAt(2), formatStringTok, argListTok)) continue; } } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) { // template int sprintf_s(char (&buffer)[size], const char *format, ...); - if (findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) { + if (findFormat(1, 2, tok->tokAt(2), formatStringTok, argListTok)) { if (!formatStringTok) continue; } // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); - else if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) { + else if (findFormat(2, 3, tok->tokAt(2), formatStringTok, argListTok)) { if (!formatStringTok) continue; } } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) { // template int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...); - if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) { + if (findFormat(2, 3, tok->tokAt(2), formatStringTok, argListTok)) { if (!formatStringTok) continue; } // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...); - else if (findFormat(3, tok->tokAt(2), formatStringTok, argListTok)) { + else if (findFormat(3, 4, tok->tokAt(2), formatStringTok, argListTok)) { if (!formatStringTok) continue; } diff --git a/test/cfg/windows.cpp b/test/cfg/windows.cpp index c01e57f6f91..5a61c9d61f2 100644 --- a/test/cfg/windows.cpp +++ b/test/cfg/windows.cpp @@ -23,6 +23,7 @@ #include #include #include +#include bool UpdateTraceACalled(TRACEHANDLE traceHandle, LPCSTR loggerName, EVENT_TRACE_PROPERTIES* pProperties) { @@ -1238,3 +1239,17 @@ void constParameterPointer_SetupDiGetDeviceInstanceId(HDEVINFO info, SP_DEVINFO_ TCHAR buffer[buffer_size]; SetupDiGetDeviceInstanceId(info, data, buffer, buffer_size, NULL); } + +int invalidPrintfArgType_sprintf_s_l() +{ + double value = 3.14; + const size_t buffer_size = 64; + char buffer[buffer_size]; + int precision = 2; + // cppcheck-suppress [checkLibraryFunction,valueFlowBailoutIncompleteVar] + _locale_t locale = _create_locale(LC_ALL, "C"); + _sprintf_s_l(buffer, buffer_size, "%.*f", locale, precision, value); + // cppcheck-suppress checkLibraryFunction + _free_locale(locale); + return 0; +} diff --git a/test/testio.cpp b/test/testio.cpp index 2d191555ba6..16d770bb0bd 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -4791,6 +4791,18 @@ class TestIO : public TestFixture { "[test.cpp:17:5]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'. [invalidPrintfArgType_s]\n" "[test.cpp:17:5]: (warning) sprintf_s format string requires 5 parameters but 6 are given. [wrongPrintfScanfArgNum]\n", errout_str()); + check("int main()\n" + "{\n" + " double value = 3.14;\n" + " const size_t buffer_size = 64;\n" + " char buffer[buffer_size];\n" + " int precision = 2;\n" + " _locale_t locale = _create_locale(LC_ALL, \"C\");\n" + " _sprintf_s_l(buffer, buffer_size, \"%.*f\", locale, precision, value);\n" + " _free_locale(locale);\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void testMicrosoftSecureScanfArgument() {