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 | ||||
---|---|---|---|---|
| ||||
#include <cstdarg>
extern "C" void f(float a, ...) {
va_list list;
va_start(list, a);
// ...
va_end(list);
} |
...
Code Block | ||||
---|---|---|---|---|
| ||||
#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 | ||||
---|---|---|---|---|
| ||||
#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 | ||||
---|---|---|---|---|
| ||||
#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 | ||||
---|---|---|---|---|
| ||||
#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 | ||||
---|---|---|---|---|
| ||||
#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" |
...