CUDASecurity-EXP01¶
Do not call va_arg with an argument of the incorrect type
Required inputs: IR
The variable arguments passed to a variadic function are accessed by calling
the
va_arg() macro. This macro accepts the
va_list representing the variable arguments of the function
invocation and the type denoting the expected argument type for the argument
being retrieved. The macro is typically invoked within a loop, being called
once for each expected argument. However, there are no type safety guarantees
that the type passed to
va_arg matches the type passed by the caller, and there are
generally no compile-time checks that prevent the macro from being invoked with
no argument available to the function call. The C Standard, 7.16.1.1,
states [
ISO/IEC
9899:2011], in part:
If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:
- one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
- one type is pointer to
voidand the other is a pointer to a character type.
Ensure that an invocation of the
va_arg() macro does not attempt to access an argument that was not
passed to the variadic function. Further, the type passed to the
va_arg() macro must match the type passed to the variadic function
after default argument promotions have been applied. Either circumstance
results in
undefined
behavior.
Noncompliant Code Example
This noncompliant code example attempts to read a variadic argument of
type
unsigned char with
va_arg(). However, when a value of type
unsigned char is passed to a variadic function, the value
undergoes default argument promotions, resulting in a value of type
int being passed.
#include <stdarg.h>
#include <stddef.h>
void func(size_t num_vargs, ...) {
va_list ap;
va_start(ap, num_vargs);
if (num_vargs > 0) {
unsigned char c = va_arg(ap, unsigned char);
// ...
}
va_end(ap);
}
void f(void) {
unsigned char c = 0x12;
func(1, c);
}
Compliant Solution
The compliant solution accesses the variadic argument with type
int, and then casts the resulting value to type
unsigned char:
#include <stdarg.h>
#include <stddef.h>
void func(size_t num_vargs, ...) {
va_list ap;
va_start(ap, num_vargs);
if (num_vargs > 0) {
unsigned char c = (unsigned char) va_arg(ap, int);
// ...
}
va_end(ap);
}
void f(void) {
unsigned char c = 0x12;
func(1, c);
}
Noncompliant Code Example
This noncompliant code example assumes that at least one variadic argument is
passed to the function, and attempts to read it using the
va_arg() macro. This pattern arises frequently when a variadic
function uses a sentinel value to denote the end of the variable argument
list. However, the caller passes no variadic arguments to the function,
which results in undefined behavior.
#include <stdarg.h>
void func(const char *cp, ...) {
va_list ap;
va_start(ap, cp);
int val = va_arg(ap, int);
// ...
va_end(ap);
}
void f(void) {
func("The only argument");
}
Compliant Solution
Standard C provides no mechanism to enable a variadic function to determine how many variadic arguments are actually provided to the function call. That information must be passed in an out-of-band manner. Oftentimes this results in the information being encoded in the initial parameter, as in this compliant solution:
#include <stdarg.h>
#include <stddef.h>
void func(size_t num_vargs, const char *cp, ...) {
va_list ap;
va_start(ap, cp);
if (num_vargs > 0) {
int val = va_arg(ap, int);
// ...
}
va_end(ap);
}
void f(void) {
func(0, "The only argument");
}
Risk Assessment
Incorrect use of
va_arg() results in undefined behavior that can include
accessing stack memory.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| EXP47-C | Medium | Likely | High | P6 | L2 |
Bibliography
| [ ISO/IEC 9899:2011] | Subclause 7.16, "Variable Arguments
<stdarg.h>"Subclause 6.5.2.2, "Function calls" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
va_arg_missing |
Call requires at least one argument for variadic parameter. |
None |
False |
va_arg_with_unpromoted_type |
Type {} does not match promoted type {}. |
None |
False |
wrong_argument_type_multiple_allowed |
Variable argument was passed type ‘{}’, which is not among the expected types. |
None |
False |
wrong_argument_type_va |
Variable arguments expect type ‘{}’, but ‘{}’ was given. |
None |
False |
Options
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
This rule has no individual options.