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

Compare with Current View Page History

« Previous Version 9 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 C99 [ISO/IEC 9899:1999], Section 7.15

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 code tries to check that none of its variable arguments are zero by passing a va_list to a helper function contains_zero(). After the call to contains_zero(), the value of ap is indeterminate, which could potentially 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

Bibliography

[ISO/IEC 9899:1999] Section 7.15, "Variable Arguments".


MSC38-C. Do not treat as an object any predefined identifier that might be implemented as a macro      49. Miscellaneous (MSC)      50. POSIX (POS)

  • No labels