VA.LIST.INDETERMINATE
Do not call va_arg() on a va_list that has an indeterminate value
The VA.LIST.INDETERMINATE checker flags instances where code causes the va_list object to have an indeterminate value.
Vulnerability and risk
The value of a va_list object can become indeterminate if it’s passed to another function and va_arg has been called within that function. If the va_list object with an indeterminate value is used again, there is a risk of unexpected behavior.
Mitigation and prevention
It’s better to create a pointer to a va_list object and pass that to another function so that the original function can use the original list when the other function returns.
Vulnerable code example
#include <stdarg.h>
#include <stdio.h>
int contains_zero(size_t count, va_list ap)
{
for (size_t i = 1; i < count; ++i) {
if (va_arg(ap, double) == 0.0) {
return 1;
}
}
return 0;
}
int print_reciprocals(size_t count, ...)
{
va_list ap;
va_start(ap, count);
if (contains_zero(count, ap)) {
va_end(ap);
return 1;
}
for (size_t i = 0; i < count; ++i) {
printf("%f ", 1.0 / va_arg(ap,double));
}
va_end(ap);
return 0;
}
In this non-compliant code example, Klocwork reports a VA.LIST.INDETERMINATE error on line 25, indicating, “Attempt to call va_arg() on a va_list that has an indeterminate value”. The va_list object ‘ap’ is passed to the contains_zero function at line 19. Because va_arg is invoked with the object at line 7, va_list ‘ap’ is indeterminate. Further, the va_list object ‘ap’ with an indeterminate value has been used again at line 25, which is undefined behavior.
Fixed code example
#include <stdarg.h>
#include <stdio.h>
int contains_zero(size_t count, va_list *ap)
{
va_list ap1;
va_copy(ap1, *ap);
for (size_t i = 1; i < count; ++i) {
if (va_arg(ap1, double) == 0.0) {
return 1;
}
}
va_end(ap1);
return 0;
}
int print_reciprocals(size_t count, ...)
{
int status;
va_list ap;
va_start(ap, count);
if (contains_zero(count, &ap)) {
printf("0 in arguments!\n");
status = 1;
} else {
for (size_t i = 0; i < count; i++) {
printf("%f ", 1.0 / va_arg(ap, double));
}
printf("\n");
status = 0;
}
va_end(ap);
return status;
}
In this fixed example, the pointer to va_list has been passed to the contains_zero() function. After this function returns, the original function is able to traverse the original list. There will be no undefined behavior.