Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

If access to the varying arguments is desired, the called function shall declare an object (generally referred to as ap in this subclause) having type va_list. The object ap may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.253
253) It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original function may take further use of the original list after the other function returns.

Noncompliant Code Example

The following noncompliant code tries attempts to check that none of its variable arguments are zero by passing a va_list to helper function contains_zero(). After the call to contains_zero(), the value of ap is indeterminate, which could cause undesired behavior.

Code Block
bgColor#FFCCCC
langc
#include <stdarg.h>
#include <stdio.h>
 
int contains_zero(intsize_t count, va_list ap) {
  for (intsize_t i = 1; i < count; ++i) {
    if (va_arg(ap, double) == 0.0) {
      return 1;
    }
  }
  return 0;
}

int print_reciprocals(intsize_t count, ...) {
  va_list ap;  
  va_start(ap, count);

  if (contains_zero(count, ap)) {
    va_end(ap);
    return 1;
  }

  for (intsize_t i = 0; i < count; ++i) {
    printf("%f ", 1.0 / va_arg(ap, double));
  }

  va_end(ap);
  return 0;
}

Compliant Solution

The compliant solution modifies print_reciprocals to create a copy of the va_list by using the va_copy macro. The copy is passed to contains_zero, and the variable arguments are accessed through the original.

Code Block
bgColor#ccccff
langc
#include <stdarg.h>
#include <stdio.h>
 
int contains_zero(intsize_t count, va_list ap) {
  for (intsize_t i = 1; i < count; ++i) {
    if (va_arg(ap, double) == 0.0) {
      return 1;
    }
  }
  return 0;
}
 
int print_reciprocals(intsize_t count, ...) {
  int status;
  va_list ap1;
  va_list ap2;  

  va_start(ap1, count);
  va_copy(ap2, ap1);

  if (contains_zero(count, ap2)) {
    status = 1;
  } else {
    for (intsize_t i = 0; i < count; i++) {
      printf("%f ", 1.0 / va_arg(ap1, double));
    }
    status = 0;
  }

  va_end(ap2);
  va_end(ap1);

  return status;
}

Risk Assessment

Reading variable arguments using a va_list that has an indeterminate value could lead to unexpected results.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MSC39-C

Low

Unlikely

Low

P3

L3

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Bibliography

[ISO/IEC 9899:2011]Subclause 7.16, "Variable Arguments <stdarg.h>"

...