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:2024], in part:

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:

—  both types are pointers to qualified or unqualified versions of compatible types;

—  one type is compatible with a signed integer type, the other type is compatible with the corresponding unsigned integer type, and the value is representable in both types;

—  one type is pointer to qualified or unqualified void and the other is a pointer to a qualified or unqualified character type;

—  or, the type of the next argument is nullptr_t and type is a pointer type that has the same representation and alignment requirements as 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

#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

#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

Automated Detection

Tool

Version

Checker

Description

Axivion Bauhaus Suite

7.2.0

CertC-EXP47



Clang
3.9
-WvarargsCan detect some instances of this rule, such as promotable types.
Cannot detect mismatched types or incorrect number of variadic arguments.



CodeSonar
8.1p0
BADMACRO.STDARG_HUse of <stdarg.h> feature


Helix QAC

2024.3

DF4901, DF4902, DF4903, DF4904





Klocwork

2024.3

CERT.VA_ARG.TYPE





LDRA tool suite
9.7.1

44 S

Enhanced Enforcement




Parasoft C/C++test

2023.1

CERT_C-EXP47-aDo not call va_arg with an argument of the incorrect type


PC-lint Plus

1.4

917

Assistance provided: reports argument promotion to match prototype




Polyspace Bug Finder

R2024a

CERT C: Rule EXP47-C


Checks for:

  • Incorrect data type passed to va_arg
  • Too many va_arg calls for current argument list

Rule fully covered




TrustInSoft Analyzer

1.38

unclassified (variadic)

Exhaustively verified (see one compliant and one non-compliant example).




Related Vulnerabilities

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

Bibliography

[ISO/IEC 9899:2024]Subclause 7.16, "Variable Arguments <stdarg.h>"



  

2 Comments

  1. In the last compliant solution function is called with invalid arguments order:

    func("The only argument", 0);

    but should be:

    func(0, "The only argument");