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
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/recommendations/expressions-exp/exp02-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

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

allowed_effects

allowed_effects

Type: set[SimpleEffect]

Default: {'exceptions', 'optimizer_hint'}

Effects that should be allowed even in right-hand operands. Note: effect 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'.