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

Compare with Current View Page History

« Previous Version 17 Next »

A variadic function – a function declared with a parameter list ending with ellipsis (...) – can accept a varying number of arguments of differing types. Variadic functions are flexible, but they are also hazardous. The compiler can't verify that a given call to a variadic function passes an appropriate number of arguments or that those arguments have appropriate types. Consequently, a runtime call to a variadic function that passes inappropriate arguments yields undefined behavior. Such undefined behavior could be exploited to run arbitrary code.

The best way to avoid calling variadic functions is the avoid defining them. However, declaring them does no harm.

When a function call expression appears in some contexts, notably as the argument in a sizeof expression, the compiler performs overload resolution to determine the result type of the call, but the object code doesn't execute the call at runtime. In such cases, the compiler uses only the function's declaration, not its definition.

Some template metaprogramming techniques that employ "substitution failure is not an error" (SFINAE) use variadic functions to implement compile-time type queries, as in:

typedef char True;
typedef struct { char a[2]; } False;

template <typename T>
True isPtr(T *);

False isPtr(...);

#define is_ptr(e) (sizeof(isPtr(e)) == sizeof(True))

In this example, is_ptr(e) returns true if expression e has a pointer type. is_ptr(e) calls variadic function isPtr(...), but the call takes place in a sizeof expression. Consequently, isPtr(...) must be declared, but it need not, and should not, be defined.

Non-compliant Code Example

This example uses a variadic function to concatenate an arbitrary number of null-terminated character sequences (NTCS) in a single NTCS. Each call to the function must use a null pointer value to mark the end of the argument list.

#include <cstdarg>

char *concatenate(char const *s, ...)
    {
    // code to actually concatenate the strings
    }

char *separator = /* some reasonable value */;

char *t = concatenate("hello", separator, "world", NULL);

Calling this function without the trailing null pointer, or with an argument of any type other than "pointer to possibly-CV-qualified char" yields undefined behavior:

char *u = concatenate("hello", separator, "world"); // undefined behavior

char *v = concatenate("hello", ' ', "world", NULL); // undefined behavior

Compliant Solution

Rather than use a variadic function, you can use a chain of binary operations:

#include <string>

string separator = /* some reasonable value */;

string s = "hello" + separator + "world";

Risk Assessment

Incorrectly using a variadic function can result in abnormal program termination, unintended information disclosure, or execution of arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

DCL38-CPP

3 (high)

2 (probable)

3 (low)

P18

L1


DCL32-CPP. Avoid runtime static initialization of objects with external linkage      02. Declarations and Initialization (DCL)      03. Expressions (EXP)

  • No labels