# V111. Call of function 'foo' with variable number of arguments. N argument has memsize type.

The analyzer found a possible error related to the transfer of the actual argument of memsize type into the function with variable number of arguments. The possible error may consist in the change of demands made to the function on the 64-bit system.

Let's examine an example.

    const char *invalidFormat = "%u";
    size_t value = SIZE_MAX;
    printf(invalidFormat, value);

The given code does not take into account that 'size_t' type does not coincide with 'unsigned' type on the 64-bit platform. It will cause the printing of the incorrect result in case if "value > UINT_MAX". The analyzer warns you that memsize type is used as an actual argument. It means that you should check the line 'invalidFormat' assigning the printing format. The correct variant may look as follows:

    const char *validFormat = "%Iu";
    size_t value = SIZE_MAX;
    printf(validFormat, value);

In the code of a real application, this error can occur in the following form, e.g.:

    wsprintf(szDebugMessage,
              _T("%s location %08x caused an access violation.\r\n"),
             readwrite,
             Exception->m_pAddr);

The second example.

    char buf[9];
    sprintf(buf, "%p", pointer);

The author of this inaccurate code did not take into account that the pointer size may excess 32 bits later. As a result, this code will cause buffer overflow on the 64-bit architecture. After checking the code on which the V111 warning message is shown you may choose one of the two ways: to increase the buffer size or rewrite the code using safe constructions.

    char buf[sizeof(pointer) * 2 + 1];
    sprintf(buf, "%p", pointer);
    // --- or ---
    std::stringstream s;
    s << pointer;

The third example.

    char buf[9];
    sprintf_s(buf, sizeof(buf), "%p", pointer);

While examining the second example you could rightly notice that in order to prevent the overflow you should use functions with security enhancements. In this case the buffer overflow won't occur but unfortunately the correct result won't be shown as well.

If the arguments types did not change their digit capacity the code is considered to be correct and warning messages won't be shown. The example:

    printf("%d", 10*5);
    CString str;
    size_t n = sizeof(float);
    str.Format(StrFormat, static_cast<int>(n));

Unfortunately, we often cannot distinguish the correct code from the incorrect one while diagnosing the described type of errors. This warning message will be shown on many of calls of the functions with variable items number even when the call is absolutely correct. It is related to the principal danger of using such C++ constructions. Most frequent problems are the problems with the use of variants of the following functions: 'printf', 'scanf', 'CString::Format'. The generally accepted practice is to refuse them and to use safe programming methods. For example, you may replace 'printf' with 'cout' and 'sprintf' with 'boost::format' or 'std::stringstream'.

Note. Eliminating false positives when working with formatted output functions

The V111 diagnostic is very simple. When the analyzer has no information about a variadic function, it warns you about every case when variable of memsize-type is passed to that function. When it does have the information, the more accurate diagnostic V576 joins in and V111 diagnostic will not issue a warning. When V576 is disabled, V111 will work in any case.

Therefore, you can reduce the number of false positives by providing the analyzer with information about the format functions. The analyzer is already familiar with such typical functions as 'printf', 'sprintf', etc., so it is user-implemented functions that you want to annotate. See the description of the V576 diagnostic for details about annotating functions.

Consider the following example. You may ask, "Why does not the analyzer output a V111 warning in case N1, but does that in case N2?"

    void OurLoggerFn(wchar_t const* const _Format, ...)
    {
      ....
    }
    void Foo(size_t length)
    {
      wprintf( L"%Iu", length );     // N1
      OurLoggerFn( L"%Iu", length ); // N2
    }

The reason is that the analyzer knows how standard function 'wprintf' works, while it knows nothing about 'OurLoggerFn', so it prefers to be overcautious and issues a warning about passing a memsize-type variable ('size_t' in this case) as an actual argument to a variadic function.

To eliminate the V111 warning, annotate the 'OurLoggerFn' function as follows:

    //+V576, function:OurLoggerFn, format_arg:1, ellipsis_arg:2
    void OurLoggerFn(wchar_t const* const _Format, ...)
    .....

Additional materials on this topic:

- 64-bit Lessons. Lesson 10. Pattern 2. Functions with variable number of arguments.