Macros are often used to execute a sequence of multiple statements as a group.
While inline functions are, in general, more suitable for this task (see PRE00-C. Prefer inline or static functions to function-like macros), occasionally they are not feasible (when macros are expected to operate on variables of different types, for example).
When multiple statements are used in a macro, they should be bound together in a do-while loop syntactically, so the macro can appear safely inside if clauses or other places that expect a single statement or a statement block.
This noncompliant code example contains multiple, unbound statements.
/* * Swaps two values. * Requires tmp variable to be defined. */ #define SWAP(x, y) \ tmp = x; \ x = y; \ y = tmp |
This macro will expand correctly in a normal sequence of statements, but not as the then-clause in an if statement:
int x, y, z, tmp; if (z == 0) SWAP( x, y); |
This will expand to
int x, y, z, tmp; if (z == 0) tmp = x; x = y; y = tmp; |
which is certainly not what the programmer intended.
This noncompliant code example inadequately bounds multiple statements.
/* * Swaps two values. * Requires tmp variable to be defined. */ #define SWAP(x,y) { tmp=x; x=y; y=tmp; } |
This macro fails to expand correctly in some case such as the following example which is meant to be an if-statement with two branches:
if (x > y) SWAP(x,y); /* Branch 1 */ else do_something(); /* Branch 2 */ |
Following macro expansion, however, this code is interpreted as an if-statement with only one branch:
if (x > y) { /* Single-branch if-statement!!! */ tmp = x; /* The one and only branch consists */ x = y; /* of the block. */ y = tmp; } ; /* empty statement */ else /* ERROR!!! "parse error before else" */ do_something(); |
The problem is the semi-colon ';' following the block.
Wrapping the macro inside a do-while loop mitigates the problem.
/* * Swaps two values. * Requires tmp variable to be defined. */ #define SWAP(x, y) \ do { \ tmp = x; \ x = y; \ y = tmp; } \ while (0) |
The do-while loop will always be executed exactly once.
Improperly wrapped statement macros can result in unexpected and difficult to diagnose behavior.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
PRE10-C |
medium |
probable |
low |
P12 |
L1 |
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
This rule appears in the C++ Secure Coding Standard as PRE10-CPP. Wrap multi-statement macros in a do-while loop.
\[[ISO/IEC PDTR 24772|AA. References#ISO/IEC PDTR 24772]\] "NMP Pre-processor Directions" [Linux Kernel Newbies FAQ|http://kernelnewbies.org/FAQ] [FAQ/DoWhile0|http://kernelnewbies.org/FAQ/DoWhile0] |