It is necessary to understand how macro replacement works in C, particularly in the context of concatenating tokens using the ##
operator and converting macro parameters to strings using the #
operator.
Concatenating Tokens
The ##
preprocessing operator is used to merge two tokens into one while expanding macros, which is called token pasting or token concatenation. When a macro is expanded, the two tokens on either side of each ##
operator are combined into a single token that replaces the ##
and the two original tokens in the macro expansion [FSF 2005 It is often useful to merge two tokens into one while expanding macros. This is called token pasting or token concatenation. The {{##}} preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each ## operator are combined into a single token, which replaces the {{##}} and the two original tokens in the macro expansion \[[FSF 05|AA. C References#FSF 05]\]. Wiki Markup
Token pasting is most useful when one or both of the tokens comes come from a macro argument. If either of the tokens next to an a ##
is a parameter name, it is replaced by its actual argument before ##
executes. The actual argument is not macro -expanded first. expanded first.
Stringification
Parameters are not replaced inside string constants, but the #
preprocessing operator can be used instead. When a macro parameter is used with a leading #
, the preprocessor replaces it with the literal text of the actual argument converted to a string constant [FSF 2005].
Noncompliant Code Example
The following definition for The following definition for {{ Wiki Markup static_assert()
}} from \[[DCL03-AC. Use a static assertion to test the value of a constant expression]] uses the {{JOIN()
}} macro to concatenate the token {{assertion_failed_at_line_
}} with the value of {{\_
\_LINE
\_
\_
}}. :
Code Block |
---|
#define JOIN(x, y) x ## y #define static_assert(e, s) \ typedef char JOIN(assertion_failed_at_line_, __LINE__) \ [(e) ? 1 : -1] |
...
{{\_
\_LINE
\_
\_
}} is a predefined macro names which expands to an integer constant representing the presumed line number of the current source line within the current source file \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\].However, the macro name that expands to an integer constant representing the presumed line number of the current source line within the current source file. If the intention is to expand the __LINE__
macro, which is likely the case here, the following definition for JOIN()
is noncompliant because the __LINE__
is not expanded, and the character array is subsequently named assertion_failed_at_line___LINE__
:
Code Block | ||||
---|---|---|---|---|
| ||||
#define JOIN(x, y) x ## y
|
Compliant Solution
. To get the macro to expand, a second level of indirection has to be added as in the following exampleis required, as shown by this compliant solution:
Code Block | ||||
---|---|---|---|---|
| ||||
#define JOIN(x, y) JOIN_AGAIN(x, y)
#define JOIN_AGAIN(x, y) x ## y
|
JOIN(x, y)
calls JOIN_AGAIN(x, y)
so that , if x
or y
is a macro, they are it is expanded before the ##
operator pastes them together.unmigrated-wiki-markup
Note also that macro parameters cannot be individually parenthesized when concatenating tokens using the {{##
}} operator, converting macro parameters to strings using the {{#
}} operator, or concatenating adjacent string literals. This is an exception to \[[PRE01-A. Use parentheses within macros around parameter names]\]exception, PRE01-C-EX2, to PRE01-C. Use parentheses within macros around parameter names.
Noncompliant Code Example
This example is noncompliant if the programmer's intent is to expand the macro before stringification:
Code Block | ||||
---|---|---|---|---|
| ||||
#define str(s) #s
#define foo 4
str(foo)
|
The macro invocation str(foo)
expands to foo
.
Compliant Solution
To stringify the result of expansion of a macro argument, two levels of macros must be used:
Code Block | ||||
---|---|---|---|---|
| ||||
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
|
The macro invocation xstr(foo)
expands to 4
because s
is stringified when it is used in str()
, so it is not macro expanded first. However, s
is an ordinary argument to xstr()
, so it is completely macro expanded before xstr()
is expanded. Consequently, by the time str()
gets to its argument, it has already been macro expanded.
Risk Assessment
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
PRE05 |
low
unlikely
medium
P1
-C | Low | Unlikely | Medium | P2 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Axivion Bauhaus Suite |
| CertC-PRE05 | |||||||
CodeSonar |
| LANG.PREPROC.HASH LANG.PREPROC.PASTE | Macro uses # operator Macro uses ## operator | ||||||
Helix QAC |
| C0341, C0342, C0801, C0802, C0803, C0811, C0872, C0880, C0881, C0884 | |||||||
Klocwork |
| MISRA.DEFINE.SHARP.ORDER.2012 | |||||||
LDRA tool suite |
| 76 S, 125 S, 637 S | Enhanced Enforcement | ||||||
PC-lint Plus |
| 9024 | Assistance provided: reports any use of pasting or stringizing operators in a macro definition |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[FSF 05|AA. C References#FSF 05]\] Section 3.5, "[Concatenation|http://gcc.gnu.org/onlinedocs/gcc-4.3.0/cpp/Concatenation.html#Concatenation]"
\[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 6.10.3, "Macro replacement," Section 6.10.3.3, "The ## operator," and Section 6.10.3.4, "Rescanning and further replacement," and Section 6.10.8, "Predefined macro names"
\[Saks 08\] Dan Saks, Stephen C. Dewhurst. Presentation. Sooner Rather Than Later: Static Programming Techniques for C++. |
Related Guidelines
SEI CERT C++ Coding Standard | VOID PRE05-CPP. Understand macro replacement when concatenating tokens or performing stringification |
Bibliography
[FSF 2005] | Section 3.4, "Stringification" Section 3.5, "Concatenation" |
[Saks 2008] |
...
PRE04-A. Do not reuse a standard header file name 01. Preprocessor (PRE) PRE06-A. Enclose header file in an inclusion guard