Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updating for how this rule interacts with DCL50-CPP

The While it is generally prohibited to define a C-style variadic function, such a function may still be defined when that function has external, C language linkage (see DCL50-CPP. Do not define a C-style variadic function for details). Under these circumstances, care must be taken when invoking the va_start() macro. The C standard library macro va_start() defines several semantic restrictions on the type of the value of its second parameter. The C Standard, subclause 7.16.1.4, paragraph 4 [ISO/IEC 9899:2011], states:

...

Passing an object of array type still produces undefined behavior in C++ because an array type as a function parameter requires use of a reference, which is prohibited. Additionally, passing an object of a type that undergoes default argument promotions still produces undefined behavior in C++ as well.

Noncompliant Code Example

In this noncompliant code example, the object passed to va_start() will undergo a default argument promotion, which results in undefined behavior:

Code Block
bgColor#FFcccc
langcpp
#include <cstdarg>
 
extern "C" void f(float a, ...) {
  va_list list;
  va_start(list, a);
  // ...
  va_end(list);
}

...

Code Block
bgColor#ccccff
langcpp
#include <cstdarg>
 
extern "C" void f(double a, ...) {
  va_list list;
  va_start(list, a);
  // ...
  va_end(list);
}

Noncompliant Code Example

In this noncompliant code example, a reference type is passed as the second argument to va_start():

Code Block
bgColor#FFcccc
langcpp
#include <cstdarg>
#include <iostream>
 
extern "C" void f(int &a, ...) {
  va_list list;
  va_start(list, a);
  if (a) {
    std::cout << a << ", " << va_arg(list, int);
    a = 100; // Assign something to a for the caller
  }
  va_end(list);
}

...

Code Block
bgColor#ccccff
langcpp
#include <cstdarg>
#include <iostream>
 
extern "C" void f(int *a, ...) {
  va_list list;
  va_start(list, a);
  if (a && *a) {
    std::cout << a << ", " << va_arg(list, int);
    *a = 100; // Assign something to *a for the caller
  }
  va_end(list);
}

Noncompliant Code Example

In this noncompliant code example, a nontrivially copyable type is passed as the second argument to va_start(), which is conditionally supported depending on the implementation:

Code Block
bgColor#FFcccc
langcpp
#include <cstdarg>
#include <iostream>
#include <string>
 
extern "C" void f(std::string s, ...) {
  va_list list;
  va_start(list, s);
  std::cout << s << ", " << va_arg(list, int);
  va_end(list);
}

...

Code Block
bgColor#ccccff
langcpp
#include <cstdarg>
#include <iostream>
 
extern "C" void f(const char *s, ...) {
  va_list list;
  va_start(list, a);
  std::cout << (s ? s : "") << ", " << va_arg(list, int);
  va_end(list);
}

...

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

Related Guidelines

Bibliography

[ISO/IEC 9899:2011]Subclause 7.16.1.4, "The va_start Macro"
[ISO/IEC 14882-2014]Subclause 18.10, "Other Runtime Support"

...