CERT-MSC39-C
Synopsis
Do not call va_arg() on a va_list that has an indeterminate value
Enabled by default
Yes
Severity/Certainty
Low/Low

Full description
Variadic functions access their variable arguments by using va_start() to initialize an object of type va_list, iteratively invoking the va_arg() macro, and finally calling va_end(). The va_list may be passed as an argument to another function, but calling va_arg() within that function causes the va_list to have an indeterminate value in the calling function. As a result, attempting to read variable arguments without reinitializing the va_list can have unexpected behavior.
Coding standards
- CERT MSC39-C
Do not call va_arg() on a va_list that has indeterminate value
Code examples
The following code example fails the check and will give a warning:
#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;
}
The following code example passes the check and will not give a warning about this issue:
#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;
}