Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

An unsafe function-like macro is one that evaluates a parameter whose expansion results in evaluating one of its parameters more than once in the code expansion or never evaluates the parameter or not at all. Never invoke an unsafe macro with arguments containing an assignment, increment, decrement, volatile access, input/output, or other expressions with side effects (including function calls, which may cause side effects).

The documentation for unsafe macros must warn about putting should warn against invoking them with arguments with side effects on the invocation, but the responsibility is on the programmer using the macro. Because of the risks associated with their use, it is recommended that you avoid the creation of unsafe macro functionsfunction-like macros be avoided. (See See PRE00-C. Prefer inline or static functions to function-like macros.)

The assert() macro is an excellent example of an unsafe macro. Its argument may be evaluated once or not at all, depending on the NDEBUG macro. For more information, see EXP31-C. Avoid side effects in assertionsThis rule is similar to EXP44-C. Do not rely on side effects in operands to sizeof, _Alignof, or _Generic.

Noncompliant Code Example

One problem with unsafe macros is is side effects on macro arguments, as shown by this noncompliant code example:

Code Block
bgColor#FFcccc
langc
#define ABS(x) (((x) < 0) ? -(x) : (x))
 
void func(int n) {
  /* ... Validate that n is within the desired range */
  int m = ABS(++n);

  /* ... */
}

The invocation of the ABS() macro in this example expands to

...

The resulting code is well defined but causes n to be incremented twice rather than once.

Compliant Solution

One In this compliant solution is simply not to invoke an unsafe macro with arguments containing an assignment, increment, decrement, or function call, as in the following example:, the increment operation ++n is performed before the call to the unsafe macro.

Code Block
bgColor#ccccff
langc
#define ABS(x) (((x) < 0) ? -(x) : (x)) /* UNSAFE */
 
void func(int n) {
  /* ... Validate that n is within the desired range */
  ++n;
  int m = ABS(n);

  /* ... */
}

Note the comment declaring warning programmers that the macro is unsafe as a warning for programmers. Alternatively, the . The macro can also be renamed ABS_UNSAFE() to make it painfully apparent clear that the macro is unsafe.

Compliant Solution

...

This compliant solution, like all the compliant solutions for this rule, has undefined behavior if the argument to ABS() is equal to the minimum (most negative) value for the signed integer type. (See INT32-C. Ensure that operations on signed integers do not result in overflow for more information.)

Compliant Solution

This compliant solution follows the guidance of PRE00-C. Prefer inline or static functions to function-like macros

...

by defining an inline function iabs() to replace the ABS() macro. Unlike the ABS() macro, which operates on operands of any type, the iabs() function will truncate arguments of types wider than int whose value is not in range of the latter type.

Code Block
bgColor#ccccff
langc
#include <complex.h>
#include <math.h>
 
static inline int absiabs(int x) {
  return (((x) < 0) ? -(x) : (x));
}
 
void func(int n) {
  /* ... Validate that n is within the desired range */

int m = absiabs(++n);

  /* ... */
}

Compliant Solution

A more flexible compliant solution is to declare the This solution eliminates the problem of recalling which macros are safe and which are not. Unlike the ABS() macro , which operates on operands of any type, the abs() function accepts arguments only of type int. C11 provides a way to overload expressions with arguments of different types, but C99 provides no such mechanism. Consequently, there is no way to achieve such genericity using the facilities of the standard C99 language. However, some C implementations provide extensions that make it possible to solve the original problem without using functionsusing a _Generic selection. To support all arithmetic data types, this solution also makes use of inline functions to compute integer absolute values. (See PRE00-C. Prefer inline or static functions to function-like macros and PRE12-C. Do not define unsafe macros.)

According to the C Standard, 6.5.1.1, paragraph 3 [ISO/IEC 9899:2011]:

The controlling expression of a generic selection is not evaluated. If a generic selection has a generic association with a type name that is compatible with the type of the controlling expression, then the result expression of the generic selection is the expression in that generic association. Otherwise, the result expression of the generic selection is the expression in the default generic association. None of the expressions from any other generic association of the generic selection is evaluated.

Because the expression is not evaluated as part of the generic selection, the use of a macro in this solution is guaranteed to evaluate the macro parameter v only once.

Code Block
bgColor#ccccff
langc
#include <complex.h>
#include <math.h>
 
static inline long long llabs(long long v) {
  return v < 0 ? -v : v;
}
static inline long labs(long v) {
  return v < 0 ? -v : v;
}
static inline int iabs(int v) {
  return v < 0 ? -v : v;
}
static inline int sabs(short v) {
  return v < 0 ? -v : v;
}
static inline int scabs(signed char v) {
  return v < 0 ? -v : v;
}
 
#define ABS(v)  _Generic(v, signed char : scabs, \
                            short : sabs, \
                            int : iabs, \
                            long : labs, \
                            long long : llabs, \
                            float : fabsf, \
                            double : fabs, \
                            long double : fabsl, \
                            double complex : cabs, \
                            float complex : cabsf, \
                            long double complex : cabsl)(v)
 
void func(int n) {
  /* Validate that n is within the desired range */
  int m = ABS(++n);
  /* ... */
}

Generic selections were introduced in C11 and are not available in C99 and earlier editions of the C Standard.

Compliant Solution (GCC)

GCC'Statement Expressions along with the s __typeof extension make makes it possible to declare and assign the value of the macro operand to a temporary of the same type and perform the computation on the temporary, thus consequently guaranteeing that the operand will be evaluated exactly once. Another GCC extension, known as statement expression, makes it possible for the block statement to appear where an expression is expected:

Code Block
bgColor#ccccff
langc
#define ABS(x)  __extension__ ({ __typeof (x) __tmp = x; __ \
                    tmp < 0 ? -__tmp : __tmp; })

Note that relying on such extensions makes code nonportable and goes against violates MSC14-C. Do not introduce unnecessary platform dependencies.

This code comes very close to violating DCL37-C. Do not declare or define a reserved identifier. It technically complies with this rule because it falls under exception DCL37-EX1. However, this code is potentially unsafe if it were invoked with a variable named __tmp. Such calling code would constitute a genuine violation of DCL37-C. Finally, this code is unsafe if it is ever invoked on a platform where __tmp actually has special meaning (see DCL37-C for more information). These are considered acceptable problems, as C provides no mechanism to declare a variable in a scope that is guaranteed to be distinct from all other variables in the same scope.

Exceptions

Noncompliant Code Example (assert())

The assert() macro is a convenient mechanism for incorporating diagnostic tests in code. (See MSC11-C. Incorporate diagnostic tests using assertions.) Expressions used as arguments to the standard assert() macro should not have side effects. The behavior of the assert() macro depends on the definition of the object-like macro NDEBUG. If the macro NDEBUG is undefined, the assert() macro is defined to evaluate its expression argument and, if the result of the expression compares equal to 0, call the abort() function. If NDEBUG is defined, assert is defined to expand to ((void)0). Consequently, the expression in the assertion is not evaluated, and no side effects it may have had otherwise take place in non-debugging executions of the code.

This noncompliant code example includes an assert() macro containing an expression (index++) that has a side effect:

Code Block
bgColor#FFCCCC
langc
#include <assert.h>
#include <stddef.h>
  
void process(size_t index) {
  assert(index++ > 0); /* Side effect */
  /* ... */
}

Compliant Solution (assert())

This compliant solution avoids the possibility of side effects in assertions by moving the expression containing the side effect outside of the assert() macro.

Code Block
bgColor#ccccff
langc
#include <assert.h>
#include <stddef.h>
  
void process(size_t index) {
  assert(index > 0); /* No side effect */
  ++index;
  /* ... */
}

Exceptions

PRE31-C-PRE31-EX1: An exception can be made for invoking an unsafe macro with a function call argument provided that the function has no side effects. However, it is easy to forget about obscure side effects that a function might have, especially library functions for which source code is not available; even changing errno is a side effect. Unless the function is user-written and does nothing but perform a computation and return its result without calling any other functions, it is likely that many developers will forget about some side effect. Consequently, although this exception is allowed, it is not recommendedmust be used with great care.

Risk Assessment

Invoking an unsafe macro with an argument that has side effects may cause those side effects to occur more than once. This practice can lead to unexpected program behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

PRE31-C

low

Low

unlikely

Unlikely

low

Low

P3

L3

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V

expanded-side-effect-multiplied
expanded-side-effect-not-evaluated
side-effect-not-expanded

Partially checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-PRE31Fully implemented
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.PREPROC.FUNCMACRO
LANG.STRUCT.SE.DEC
LANG.STRUCT.SE.INC

Function-Like Macro
Side Effects in Expression with Decrement
Side Effects in Expression with Increment

Coverity

Include Page
Coverity_V
Coverity_V

ASSERT_SIDE_EFFECTS

Partially implemented

Can detect the specific instance where assertion contains an operation/function call that may have a side effect

ECLAIR
Include Page
ECLAIR_V
ECLAIR_V
CC2.EXP31
CC2.PRE31
Fully implemented
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C3462, C3463, C3464,C3465,C3466,C3467

C++3225, C++3226, C++3227, C++3228, C++3229 

Fully implemented
Klocwork

Include Page
Klocwork_V
Klocwork_V

PORTING.VAR.EFFECTSFully implemented
LDRA tool suite
Include Page
LDRA_V
LDRA_V

9 S


, 562 S, 572 S

Partially implemented

PRQA QA-C Include PagePRQA_VPRQA_V

3454
3455
3456

, 35 D, 1 Q

Fully implemented

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-PRE31-b
CERT_C-PRE31-c
CERT_C-PRE31-d

Assertions should not contain assignments, increment, or decrement operators
Assertions should not contain function calls nor function-like macro calls
Avoid side effects in arguments to unsafe macros

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

666, 2666

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule PRE31-C

Checks for side effect in arguments to unsafe macro (rule partially covered)


RuleChecker
Include Page
RuleChecker_V
RuleChecker_V

expanded-side-effect-multiplied
expanded-side-effect-not-evaluated
side-effect-not-expanded

Partially checked
Fully implemented

Related Vulnerabilities

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

Related Guidelines

 Bibliography

Key here (explains table format and definitions)

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

Bibliography

[Dewhurst 2002]Gotcha #28, "Side Effects in Assertions"
[ISO/IEC 9899:2011]Subclause 6.5.1.1, "Generic Selection" 
[Plum 1985]Rule 1-11

...


...

Image Modified Image Modified Image Modified