CERT.VA_START.TYPE

The type of the argument passed to va_start() must have well-defined behaviour across implementations. Specifically, it should not be

  • a reference
  • an array
  • a class with a non-trivial constructor or destructor
  • one of the types that undergoes default promotion when passed as an argument to a function without a prototype or as a trailing argument to a variadic function

The CERT.VA_START.TYPE checker flags cases where an unsupported object type is passed to va_start() as the second argument.

Vulnerability and risk

Passing an object of an unsupported type as the second argument to va_start() can result in undefined behavior that may cause data integrity violations.

Mitigation and prevention

Ensure the second argument passed to va_start() is a supported type.

Vulnerable code example #1

Copy
   #include <cstdarg>

   extern "C" void f(float a, ...) {
     va_list list;
     va_start(list, a);
     // ...
     va_end(list);
   }

In this noncompliant example, Klocwork reports a CERT.VA_START.TYPE defect on line 5, because the incorrect type is passed as the second argument to va_start(). This is because after default argument promotion, the type ‘float’ is converted to ‘double’, which can result in undefined behavior.

Fixed code example

Copy
   #include <cstdarg>

   extern "C" void f(double a, ...) {
     va_list list;
     va_start(list, a);
     // ...
     va_end(list);
   }

Vulnerable code example #2

Copy
    #include <cstdarg>
    #include <iostream>

    extern "C" void f(int &a, ...) {
       va_list list;
       va_start(list, a);
       if (a) {
           std::cout << a << ", " << va_arg(list, int);
           a = 100; // Assign something to a for the caller
      }
      va_end(list);
   }

In this noncompliant example, Klocwork reports a CERT.VA_START.TYPE defect on line 6, because the reference type is passed as the second argument to va_start(), which may result in undefined behavior.

Fixed code example #2

Copy
   #include <cstdarg>
   #include <iostream>

   extern "C" void f(int *a, ...) {
     va_list list;
     va_start(list, a);
     if (a && *a) {
       std::cout << a << ", " << va_arg(list, int);
       *a = 100; // Assign something to *a for the caller
     }
     va_end(list);
  }

The above code is compliant because the correct type, ‘double’, is passed as the second argument to va_start().

Vulnerable code example #3

Copy
    #include <cstdarg>
    #include <iostream>
    #include <string>

    extern "C" void f(std::string s, ...) {
      va_list list;
      va_start(list, s);
      std::cout << s << ", " << va_arg(list, int);
      va_end(list
   }

In this noncompliant example, Klocwork reports a CERT.VA_START.TYPE defect on line 7 because a nontrivial copy constructor is passed as the second argument to va_start(), which may result in undefined behavior.

Fixed code example #3

Copy
    #include <cstdarg>
    #include <iostream>

    extern "C" void f(const char *s, ...) {
       va_list list;
       va_start(list, s);
       std::cout << (s ? s : "") << ", " << va_arg(list, int);
       va_end(list);
    }

The above code is compliant because it passes const char* instead of std::string, which has a well-defined implementation.

Related checkers