An unsafe macro function is one that evaluates a parameter more than once in the code expansion. Never invoke an unsafe macro with arguments containing an assignment, increment, decrement, volatile access, or other side effects including function calls which may cause side effects. Any input or output is also a side effect and must similarly be avoided in arguments to unsafe macros. Any input or output is also a side effect, although this is commonly accomplished through function calls or volatile access.
The documentation for unsafe macros must warn about putting side effects on the invocation, and 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 also PRE00-A. Prefer inline functions to macros.
Non-Compliant Coding Example
One problem with unsafe macros is side effects on macro arguments. The following non-compliant code, which increments n twice, is a typical example:
#define ABS(x) (((x) < 0) ? -(x) : (x)) /* ... */ m = ABS(++n); /* 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)) /* ... */ ++n; m = ABS(n);
A second, preferable, compliant solution is to declare ABS() as an inline function (see PRE00-A. Prefer inline functions to 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 calling functions that have 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 |
1 (low) |
1 (unlikely) |
2 (medium) |
P2 |
L3 |
Automated Detection
The LDRA tool suite V 7.6.0 is able to detect violations of this rule.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[ISO/IEC 9899-1999]] Section 5.1.2.3, "Program execution"
[[Plum 85]] Rule 1-11
01. Preprocessor (PRE) 02. Declarations and Initialization (DCL)