CertC-EXP02¶
Be aware of the short-circuit behavior of the logical AND and OR operators
Required inputs: IR
The logical AND and logical OR operators (
&& and
||, respectively) exhibit "short-circuit" operation. That is, the
second operand is not evaluated if the result can be deduced solely by
evaluating the first operand.
Programmers should exercise caution if the second operand contains side effects because it may not be apparent whether the side effects actually occur.
In the following code, the value of
i is incremented only when
i >= 0:
enum { max = 15 };
int i = /* Initialize to user-supplied value */;
if ( (i >= 0) && ( (i++) <= max) ) {
/* Code */
}
Although the behavior is well defined, it is not immediately obvious whether or
not
i gets incremented.
Noncompliant Code Example
In this noncompliant code example, the second operand of the logical OR operator invokes a function that results in side effects:
char *p = /* Initialize; may or may not be NULL */
if (p || (p = (char *) malloc(BUF_SIZE)) ) {
/* Perform some computation based on p */
free(p);
p = NULL;
} else {
/* Handle malloc() error */
return;
}
Because
malloc() is called only if
p is
NULL when entering the
if clause,
free() might be called with a pointer to local data not allocated
by
malloc(). (See
MEM34-C.
Only free memory allocated dynamically.) This behavior is partially due to
the uncertainty of whether or not
malloc() is actually called.
Compliant Solution
In this compliant solution, a second pointer,
q, is used to indicate whether
malloc() is called; if not,
q remains set to
NULL. Passing
NULL to
free() is guaranteed to safely do nothing.
char *p = /* Initialize; may or may not be NULL */
char *q = NULL;
if (p == NULL) {
q = (char *) malloc(BUF_SIZE);
p = q;
}
if (p == NULL) {
/* Handle malloc() error */
return;
}
/* Perform some computation based on p */
free(q);
q = NULL;
Risk Assessment
Failing to understand the short-circuit behavior of the logical OR or AND operator may cause unintended program behavior.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| EXP02-C | Low | Unlikely | Medium | P2 | L3 |
Related Guidelines
| SEI CERT C++ Coding Standard | VOID EXP02-CPP. Be aware of the short-circuit behavior of the logical AND and OR operators |
| MITRE CWE | CWE-768, Incorrect short circuit evaluation |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
call_with_side_effect |
Call in right-hand operand of {} may have side-effect |
None |
False |
modifies_local_var |
Right-hand operand of {} modifies ‘{}’ |
None |
False |
side_effect |
Right-hand operand of {} has side-effect |
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
allowed_effects¶
allowed_effects
Effects that should be allowed even in right-hand operands. Note: effectType: set[SimpleEffect]
Default:
{'exceptions', 'optimizer_hint'}
SimpleEffect.unknown_code is implicitly added during
single-file analysis.
Option Types¶
These types are used by options listed above:
SimpleEffect¶
This enum is a simplified representation of the effects. It's used by stylecheck rules as `Set[SimpleEffect]` to allow the user to configure additional allowed effects.alloc_free
Memory allocations (malloc, free, new, delete).volatile
Accessing volatile memory.atomic
Atomic access; also used for inter-thread memory barriers.unknown_code
The "Unknown Code" effect appears when calling a function that has no definition in the IR.exceptions
Throwing C++ exceptions.optimizer_hint
Optimizer hints without semantic effect. Examples are '__builtin_assume' or '__builtin_unreachable'.