VA.LIST.INDETERMINATE
va_list 具有未定值时请勿调用其中的 va_arg()
VA.LIST.INDETERMINATE 检查器会标记其中的代码导致 va_list 对象具有未定值的实例。
漏洞与风险
如果 va_list 对象传递给另一个函数并且在该函数中调用了 va_arg,则 va_list 对象的值可能变为未定值。如果再次使用具有未定值的 va_list 对象,则有出现意外行为的风险。
缓解与预防
最好创建一个指向 va_list 对象的指针,并将其传递给另一个函数,以便原始函数可以在其他函数返回时使用原始列表。
漏洞代码示例
复制
#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;
}
在此不符合要求的代码示例中,Klocwork 在第 25 行报告 VA.LIST.INDETERMINATE 错误,这表示“尝试在具有未定值的 va_list 中调用 va_arg()”。va_list 对象 ap 在第 19 行传递给了 contains_zero 函数。因为在第 7 行通过该对象调用了 va_arg,而 va_list 对象 ap 不确定。此外,在第 25 行再次使用了具有未定值的 va_list 对象 ap,此为未定义的行为。
修正代码示例
复制
#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;
}
在此修正代码示例中,指向 va_list 的指针已传递给 contains_zero() 函数。在此函数返回后,原始函数能够遍历原始列表。不会发生未定义的行为。