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

1   #include <cstdarg>
2
3   extern "C" void f(float a, ...) {
4     va_list list;
5     va_start(list, a);
6     // ...
7     va_end(list);
8   } 

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

1   #include <cstdarg>
2
3   extern "C" void f(double a, ...) {
4     va_list list;
5     va_start(list, a);
6     // ...
7     va_end(list);
8   }

Vulnerable code example #2

1    #include <cstdarg>
2    #include <iostream>
3
4    extern "C" void f(int &a, ...) {
5       va_list list;
6       va_start(list, a);
7       if (a) {
8           std::cout << a << ", " << va_arg(list, int);
9           a = 100; // Assign something to a for the caller
10      }
11      va_end(list);
12   }

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

1   #include <cstdarg>
2   #include <iostream>
3
4   extern "C" void f(int *a, ...) {
5     va_list list;
6     va_start(list, a);
7     if (a && *a) {
8       std::cout << a << ", " << va_arg(list, int);
9       *a = 100; // Assign something to *a for the caller
10     }
11     va_end(list);
12  }

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

Vulnerable code example #3

1    #include <cstdarg>
2    #include <iostream>
3    #include <string>
4
5    extern "C" void f(std::string s, ...) {
6      va_list list;
7      va_start(list, s);
8      std::cout << s << ", " << va_arg(list, int);
9      va_end(list
10   }

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

1    #include <cstdarg>
2    #include <iostream>
3
4    extern "C" void f(const char *s, ...) {
5       va_list list;
6       va_start(list, s);
7       std::cout << (s ? s : "") << ", " << va_arg(list, int);
8       va_end(list);
9    } 

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

Related checkers

External guidance