Functions can be defined to accept more formal arguments at the call site than are specified by the parameter declaration clause. Such functions are called variadic functions because they can accept a variable number of arguments from a caller. C++ provides two mechanisms by which a variadic function can be defined: function parameter packs and use of a C-style ellipsis as the final parameter declaration.
Variadic functions are flexible in that because they accept a varying number of arguments of differing types. However, they can also be hazardous. A variadic function using a C-style ellipsis (hereafter called a C-style variadic function) has no mechanisms to check the type safety of arguments being passed to the function or to check that the number of arguments being passed matches the semantics of the function definition. Consequently, a runtime call to a C-style variadic function that passes inappropriate arguments yields undefined behavior. Such undefined behavior could be exploited to run arbitrary code.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <type_traits>
template <typename Arg, typename std::enable_if<std::is_integral<Arg>::value>::type * = nullptr>
int add(Arg f, Arg s) { return f + s; }
template <typename Arg, typename... Ts, typename std::enable_if<std::is_integral<Arg>::value>::type * = nullptr>
int add(Arg f, Ts... rest) {
return f + add(rest...);
}
|
Note that this This compliant solution makes use of std::enable_if to ensure that any nonintegral argument value results in an ill-formed program.
...
DCL50-CPP-EX2: As stated in the normative text, C-style variadic functions that are declared but never defined are permitted. For example, when a function call expression appears in an unevaluated context, such as the argument in a sizeof expression, overload resolution is performed to determine the result type of the call but does not require a function definition. Some template metaprogramming techniques that employ SFINAE use variadic function declarations to implement compile-time type queries, as in the following :example.
| Code Block | ||||
|---|---|---|---|---|
| ||||
template <typename Ty>
class has_foo_function {
typedef char yes[1];
typedef char no[2];
template <typename Inner>
static yes& test(Inner *I, decltype(I->foo()) * = nullptr); // Function is never defined.
template <typename>
static no& test(...); // Function is never defined.
public:
static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
}; |
In this example, the value of value is determined on the basis of which overload of test() is selected. The declaration of Inner *I allows use of the variable I within the decltype specifier, which results in a pointer of some (possibly void) type, with a default value of nullptr. However, if there is no declaration of Inner::foo(), the decltype specifier will be ill-formed, and that variant of test() will not be a candidate function for overload resolution due to SFINAE. The result is that the C-style variadic function variant of test() will be the only function in the candidate set. Both test() functions are declared , but never defined , because their definitions are not required for use within an unevaluated expression context.
Risk Assessment
Incorrectly using a variadic function can result in abnormal program termination, unintended information disclosure, or execution of arbitrary code.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level | |
|---|---|---|---|
DCL50-CPP | High | Probable | Yes |
No | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| function-ellipsis | Fully checked | ||||||
| Axivion Bauhaus Suite |
| CertC++-DCL50 | |||||||
| Clang |
| cert-dcl50-cpp | Checked by clang-tidy. | ||||||
| CodeSonar |
| LANG.STRUCT.ELLIPSIS
| Ellipsis | ||||||
| Helix QAC |
| C++2012, C++2625 | |||||||
| Klocwork |
| MISRA.FUNC.VARARG | |||||||
| LDRA tool suite |
| 41 S | Fully Implemented | ||||||
| Parasoft C/C++test |
| CERT_CPP-DCL50-a | Functions shall not be defined with a variable number of arguments | |||||||
| Polyspace Bug Finder |
| CERT C++: DCL50-CPP | Checks for function definition with ellipsis notation (rule fully covered) | ||||||
| RuleChecker |
| function-ellipsis | Fully checked | ||||||
| Security Reviewer - Static Reviewer |
| UNSAFE_09 | Fully implemented |
2012
2625
| SonarQube C/C++ Plugin |
| FunctionEllipsis |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
...