The parameters of a variadic function are interpreted by the {{The variable parameters of a variadic function—that is, those that correspond with the position of the ellipsis—are interpreted by the Wiki Markup va_arg()
}} macro. The {{va_arg()
}} macro is used to extract the next argument from an initialized argument list within the body of a variadic function implementation. The size of each parameter is determined by the specified {{ type}}. If {{type}} is inconsistent with the corresponding argument, the behavior is undefined and may result in misinterpreted data or an alignment error \[[the type is inconsistent with the corresponding argument, the behavior is undefined and may result in misinterpreted data or an alignment error (see EXP36-C. Do not cast between pointers to objects or types with differing alignments]\].pointers into more strictly aligned pointer types).
The variable arguments to a variadic function are not checked for type by the compiler. As a resultBecause arguments to variadic functions are untyped, the programmer is responsible for ensuring that they are the same type as compatible with the corresponding parameter 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
void
and the other is a pointer to a character type.
Non-Compliant Code Example (type interpretation error)
after the default argument promotions:
- Integer arguments of types ranked lower than
int
are promoted toint
ifint
can hold all the values of that type; otherwise, they are promoted tounsigned int
(the integer promotions). - Arguments of type
float
are promoted todouble
.
Noncompliant Code Example (Type Interpretation Error)
The C The C99 printf()
function is implemented as a variadic function. This non-compliant noncompliant code example swaps its null-terminated byte string and integer parameters with respect to how they were are specified in the format string. Consequently, the integer is interpreted as a pointer to a NULL null-terminated byte string and dereferenced. This , which will likely cause the program to abnormally terminate. Note that the error_message
pointer is likewise interpreted as an integer.
Code Block | ||||
---|---|---|---|---|
| ||||
const char *error_msg = "Error occurred";
/* ... */
printf("%s:%d", 15, error_msg);
|
Compliant Solution (
...
Type Interpretation Error)
This compliant solution is formatted modifies the format string so that the specifiers are consistent with their parameters.conversion specifiers correspond to the arguments:
Code Block | ||||
---|---|---|---|---|
| ||||
const char *error_msg = "Error occurred";
/* ... */
printf("%d:%s", 15, error_msg);
|
As shown, care must be taken to ensure that the arguments passed to a format string function match up with the supplied format string.
...
Noncompliant Code Example (
...
Type Alignment Error)
In this non-compliant noncompliant code example, a type long long
integer is incorrectly parsed by the printf()
function with a %d
specifier. This code may result in data truncation or misrepresentation when the value is extracted from the argument list.
Code Block | ||||
---|---|---|---|---|
| ||||
long long a = 1; const char msg[128] = "Default message"; /* ... */ printf("%d %s", a, msg); |
Because a long long
was not interpreted, if the long long
uses more bytes for storage, the subsequent format specifier %s
is unexpectedly offset, causing unknown data to be used instead of the pointer to the message.
Compliant Solution (
...
Type Alignment Error)
This compliant solution adds the length modifier ll
to the %d
format specifier so that the variadic function parser for printf()
extracts the correct number of bytes from the variable argument list for the long long
argument.:
Code Block | ||||
---|---|---|---|---|
| ||||
long long a = 1; const char msg[128] = "Default message"; /* ... */ printf("%lld %s", a, msg); |
Noncompliant Code Example (NULL
)
The C Standard allows NULL to be either an integer constant or a pointer constant. While passing NULL as an argument to a function with a fixed number of arguments will cause NULL to be cast to the appropriate pointer type, when it is passed as a variadic argument, this will not happen if sizeof(NULL) != sizeof(void *).
This is possible for several reasons:
- Pointers and ints may have different sizes on a platform where NULL is an integer constant
- The platform may have different pointer types with different sizes on a platform. In that case, if NULL is a void pointer, it is the same size as a pointer to char (C11 section 6.2.5, paragraph 28), which might be sized differently than the required pointer type.
On either such platform, the following code will have undefined behavior:
Code Block | ||||
---|---|---|---|---|
| ||||
char* string = NULL;
printf("%s %d\n", string, 1);
|
On a system with 32-bit int
and 64-bit pointers, printf()
may interpret the NULL
as high-order bits of the pointer and the third argument 1
as the low-order bits of the pointer. In this case, printf()
will print a pointer with the value 0x00000001
and then attempt to read an additional argument for the %d
conversion specifier, which was not provided.
Compliant Solution (NULL
)
This compliant solution avoids sending NULL
to printf()
:
Code Block | ||||
---|---|---|---|---|
| ||||
char* string = NULL;
printf("%s %d\n", (string ? string : "null"), 1);
|
Risk Assessment
Inconsistent typing in variadic functions can result in abnormal program termination or unintended information disclosure.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
DCL11- |
2 (medium)
2 (probable)
2 (medium)
P8
C | High | Probable | High | P6 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Axivion Bauhaus Suite |
| CertC-DCL11 | |||||||
CodeSonar |
| LANG.STRUCT.ELLIPSIS | Ellipsis | ||||||
Compass/ROSE | Does not currently detect violations of this recommendation. Although the recommendation in general cannot be automated, because of the difficulty in enforcing contracts between a variadic function and its invokers, it would be fairly easy to enforce type correctness on arguments to the | ||||||||
| CC2.DCL11 | Partially implemented | |||||||
GCC |
| Warns about inconsistently typed arguments to formatted output functions when the | |||||||
Helix QAC |
| C0179, C0184, C0185, C0186, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0206, C0207, C0208 | |||||||
Klocwork |
| MISRA.FUNC.VARARG SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT.SCAN | |||||||
LDRA tool suite |
| 41 S, 589 S | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_C-DCL11-a |
| ||||||
Parasoft Insure++ | Runtime analysis | ||||||||
PC-lint Plus |
| 175, 559, 2408 | Assistance provided: reports issues involving format strings | ||||||
Polyspace Bug Finder |
| Checks for format string specifiers and arguments mismatch (rec. partially covered) | |||||||
PVS-Studio |
| V576 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule recommendation on the CERT website.
References
Related Guidelines
ISO/IEC TR 24772:2013 | Type System [IHN] Subprogram Signature Mismatch [OTR] |
MISRA C:2012 | Rule 17.1 (required) |
...
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.15, "Variable arguments" Wiki Markup