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() 函数。在此函数返回后,原始函数能够遍历原始列表。不会发生未定义的行为。