You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 63 Next »

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

The documentation for unsafe macros must warn about putting 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 functions. (See recommendation 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 rule EXP31-C. Avoid side effects in assertions.

Noncompliant Code Example

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

#define ABS(x) (((x) < 0) ? -(x) : (x))
/* ... */
m = ABS(++n); /* undefined behavior */

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

m = (((++n) < 0) ? -(++n) : (++n));  /* undefined behavior */

The resulting code also violates rule EXP30-C. Do not depend on order of evaluation between sequence points, resulting in undefined behavior.

Compliant Solution

One 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:

#define ABS(x) (((x) < 0) ? -(x) : (x)) /* UNSAFE */
/* ... */
++n;
m = ABS(n);

Note the comment declaring the macro unsafe as a warning for programmers. Alternatively, the macro can be renamed ABS_UNSAFE() to make it painfully apparent that the macro is unsafe. However, a preferable, compliant solution is to declare ABS() as an inline function. (See recommendation PRE00-C. Prefer inline or static functions to function-like macros.)

inline int abs(int x) {
  return (((x) < 0) ? -(x) : (x));
}
/* ... */
m = abs(++n);

This eliminates the problem of recalling which macros are safe and which are not.

Exceptions

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, while this exception is allowed, it is not recommended.

Risk Assessment

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

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

PRE31-C

low

unlikely

medium

P2

L3

Automated Detection

Tool

Version

Checker

Description

9.7.1

 

 

Related Vulnerabilities

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

Related Guidelines

CERT C++ Secure Coding Standard: PRE31-CPP. Avoid side-effects in arguments to unsafe macros

ISO/IEC 9899:1999 Section 5.1.2.3, "Program execution"

ISO/IEC TR 24772 "NMP Pre-processor Directions"

MISRA Rule 19.6

Bibliography

[Plum 1985] Rule 1-11


      01. Preprocessor (PRE)      PRE32-C. Do not use preprocessor directives inside macro arguments

  • No labels