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
#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
#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.