You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 15 Next »

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. According to the C Standard [ISO/IEC 9899:2011], section 7.16,

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.

Noncompliant Code Example

The following noncompliant code tries 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.

int contains_zero(int count, va_list ap){
  int i;

  for(i=1;i<count;i++){
    if(va_arg(ap, double) == 0.0)
      return 1;
  }

  return 0;
}


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

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

  for(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.

int print_reciprocals(int 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 (int 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.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

MSC39-C

low

unlikely

low

P3

L3

Related Guidelines

ISO/IEC 9899:2011 Section 7.16, "Variable arguments <stdarg.h>"

 


  • No labels