CUDASecurity-EXP02¶
Do not rely on side effects in operands to sizeof, _Alignof, or _Generic
Required inputs: IR
Some operators do not evaluate their operands beyond the type information the operands provide. When using one of these operators, do not pass an operand that would otherwise yield a side effect since the side effect will not be generated.
The
sizeof operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. In most cases,
the operand is not evaluated. A possible exception is when the type of
the operand is a variable length array type (VLA); then the expression is
evaluated. When part of the operand of the sizeof operator is a VLA type and
when changing the value of the VLA's size expression would not affect the
result of the operator, it is
unspecified
whether or not the size expression is evaluated. (See
unspecified
behavior 22.)
The operand passed to
_Alignof is never evaluated, despite not being an expression. For
instance, if the operand is a VLA type and the VLA's size expression contains a
side effect, that side effect is never evaluated.
The operand used in the controlling expression of a
_Generic selection expression is never evaluated.
Providing an expression that appears to produce side effects may be misleading
to programmers who are not aware that these expressions are not evaluated, and
in the case of a VLA used in
sizeof, have unspecified results. As a result, programmers
may make invalid assumptions about program state, leading to errors and
possible software
vulnerabilities.
This rule is similar to PRE31-C. Avoid side effects in arguments to unsafe macros.
Noncompliant Code Example (
sizeof)
In this noncompliant code example, the expression
a++ is not evaluated:
#include <stdio.h>
void func(void) {
int a = 14;
int b = sizeof(a++);
printf("%d, %d\n", a, b);
}
Consequently, the value of
a after
b has been initialized is 14.
Compliant Solution (
sizeof)
In this compliant solution, the variable
a is incremented outside of the
sizeof operation:
#include <stdio.h>
void func(void) {
int a = 14;
int b = sizeof(a);
++a;
printf("%d, %d\n", a, b);
}
Noncompliant Code Example (
sizeof, VLA)
In this noncompliant code example, the expression
++n in the initialization expression of
a must be evaluated because its value affects the size of
the VLA operand of the
sizeof operator. However, in the initialization expression of
b, the expression
++n % 1 evaluates to
0. This means that the value of
n does not affect the result of the
sizeof operator. Consequently, it is unspecified whether or
not
n will be incremented when initializing
b.
#include <stddef.h>
#include <stdio.h>
void f(size_t n) {
/* n must be incremented */
size_t a = sizeof(int[++n]);
/* n need not be incremented */
size_t b = sizeof(int[++n % 1 + 1]);
printf("%zu, %zu, %zu\n", a, b, n);
/* ... */
}
Compliant Solution (
sizeof, VLA)
This compliant solution avoids changing the value of the variable
n used in each
sizeof expression and instead increments
n safely afterwards:
#include <stddef.h>
#include <stdio.h>
void f(size_t n) {
size_t a = sizeof(int[n + 1]);
++n;
size_t b = sizeof(int[n % 1 + 1]);
++n;
printf("%zu, %zu, %zu\n", a, b, n);
/* ... */
}
Noncompliant Code Example (
_Generic)
This noncompliant code example attempts to modify a variable's value as part of
the
_Generic selection control expression. The programmer may expect
that
a is incremented, but because
_Generic does not evaluate its control expression, the value
of
a is not modified.
#include <stdio.h>
#define S(val) _Generic(val, int : 2, \
short : 3, \
default : 1)
void func(void) {
int a = 0;
int b = S(a++);
printf("%d, %d\n", a, b);
}
Compliant Solution (
_Generic)
In this compliant solution, a is incremented outside of the
_Generic selection expression:
#include <stdio.h>
#define S(val) _Generic(val, int : 2, \
short : 3, \
default : 1)
void func(void) {
int a = 0;
int b = S(a);
++a;
printf("%d, %d\n", a, b);
}
Noncompliant Code Example (
_Alignof)
This noncompliant code example attempts to modify a variable while getting its
default alignment value. The user may have expected
val to be incremented as part of the
_Alignof expression, but because
_Alignof does not evaluate its operand,
val is unchanged.
#include <stdio.h>
void func(void) {
int val = 0;
/* ... */
size_t align = _Alignof(int[++val]);
printf("%zu, %d\n", align, val);
/* ... */
}
Compliant Solution (
_Alignof)
This compliant solution moves the expression out of the
_Alignof operator:
#include <stdio.h>
void func(void) {
int val = 0;
/* ... */
++val;
size_t align = _Alignof(int[val]);
printf("%zu, %d\n", align, val);
/* ... */
}
Exceptions
EXP44-C-EX1: Reading a
volatile-qualified value is a side-effecting operation. However,
accessing a value through a
volatile-qualified type does not guarantee side effects will
happen on the read of the value unless the underlying object is also
volatile-qualified. Idiomatic reads of a
volatile-qualified object are permissible as an operand to a
sizeof(),
_Alignof(), or
_Generic expression, as in the following example:
void f(void) {
int * volatile v;
(void)sizeof(*v);
}
Risk Assessment
If expressions that appear to produce side effects are supplied to an operator that does not evaluate its operands, the results may be different than expected. Depending on how this result is used, it can lead to unintended program behavior.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| EXP44-C | Low | Unlikely | Low | P3 | L3 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C | EXP52-CPP. Do not rely on side effects in unevaluated operands | Prior to 2018-01-12: CERT: Unspecified Relationship |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
side_effect_in_alignof |
Operand of “_Alignof” shall not contain side effects |
None |
False |
side_effect_in_generic |
Selector of “_Generic” shall not contain side effects |
None |
False |
side_effect_in_sizeof |
Operand of “sizeof” shall not contain side effects |
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
allow_in_macro_definition¶
allow_in_macro_definition : bool = False