CERT.VA_ARG.TYPE

The type passed to a va_arg() macro must match the type passed to a variadic function after default argument promotions. Specifically, C and C++ will promote variadic float arguments to double and smaller integral types to int. As such, it is never correct to call va_arg() with the type float or one of the smaller integer types (for example, unsigned char, short, and so on).

The CERT.VA_ARG.TYPE checker flags cases where the type passed to va_arg() does not match the type passed to a variadic function after argument promotions.

Vulnerability and risk

The behavior is undefined if the type passed to va_arg() is not matching with the type passed to variadic function after default argument promotions.

Mitigation and prevention

Do not call the va_arg() macro with an argument of the incorrect type (such as ‘short int’, ‘unsigned short int’, ‘char’, ‘unsigned char ’, ‘signed char’ , or ‘float’).

Vulnerable code example

Copy
    #include <stdarg.h>
    #include <stddef.h>

    void func1(size_t num_vargs, ...)
    {
        va_list ap;
        va_start(ap, num_vargs);
        if (num_vargs > 0) {
            unsigned char c = va_arg(ap, unsigned char);
           // ...
       }
       va_end(ap);
   }

   void func2(size_t num_vargs, ...)
   {
       va_list ap;
       va_start(ap, num_vargs);
       if (num_vargs > 0) {
           float var = va_arg(ap, float);
           // ...
       }
       va_end(ap);
   }

   void f(void)
   {
       unsigned char c = 0x12;
       float d =1.25;
       func1(1, c);
       func2(1, d);
   }

In this noncompliant example, Klocwork reports a CERT.VA_ARG.TYPE defect on line 9 and line 20, because incorrect types are passed as arguments to the va_arg() macro. This, is because after default argument promotion, the type ‘unsigned char’ is converted to ‘int’ , and ‘float’ is converted to ‘double’ which can result in undefined behavior.

Fixed code example

Copy
     #include <stdarg.h>
     #include <stddef.h>

     void func1(size_t num_vargs, ...)
     {
         va_list ap;
         va_start(ap, num_vargs);
         if (num_vargs > 0) {
             unsigned char c = (unsigned char) va_arg(ap, int);
            // ...
        }
        va_end(ap);
    }

    void func2(size_t num_vargs, ...)
    {
        va_list ap;
        va_start(ap, num_vargs);
        if (num_vargs > 0) {
            double var1 =  va_arg(ap, double);
            // ...
        }
        va_end(ap);
    }

    void f(void)
    {
        int    i = 10;
        double d = 10.234;
        func1(1, i);
        func2(1, d);
    }

The above code is compliant because correct types (such as ‘int’ or ‘double’) are passed to the va_arg() macro.

Related checkers

External guidance