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
#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
#include <cstdarg>
extern "C" void f(double a, ...) {
va_list list;
va_start(list, a);
// ...
va_end(list);
}
Vulnerable code example #2
#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
#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
#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
#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.