CertC-MSC38¶
Do not treat a predefined identifier as an object if it might only be implemented as a macro
Required inputs: IR
The C Standard, 7.1.4 paragraph 1, [ ISO/IEC 9899:2011] states
Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro. Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro.185
185) This means that an implementation shall provide an actual function for each library function, even if it also provides a macro for that function.
However, the C Standard enumerates specific exceptions in which the
behavior of accessing an object or function expanded to be a standard library
macro definition is
undefined.
The macros are
assert,
errno,
math_errhandling,
setjmp,
va_arg,
va_copy,
va_end, and
va_start. These cases are described by
undefined
behaviors
110,
114,
122,
124,
and
138.
Programmers must not suppress these macros to access the underlying object or
function.
Noncompliant Code Example (
assert)
In this noncompliant code example, the standard
assert() macro is suppressed in an attempt to pass it as a
function pointer to the
execute_handler() function. Attempting to suppress the
assert() macro is
undefined
behavior.
#include <assert.h>
typedef void (*handler_type)(int);
void execute_handler(handler_type handler, int value) {
handler(value);
}
void func(int e) {
execute_handler(&(assert), e < 0);
}
Compliant Solution (
assert)
In this compliant solution, the
assert() macro is wrapped in a helper function, removing the
undefined
behavior:
#include <assert.h>
typedef void (*handler_type)(int);
void execute_handler(handler_type handler, int value) {
handler(value);
}
static void assert_handler(int value) {
assert(value);
}
void func(int e) {
execute_handler(&assert_handler, e < 0);
}
Noncompliant Code Example (Redefining
errno)
Legacy code is apt to include an incorrect declaration, such as the following in this noncompliant code example:
extern int errno;
Compliant Solution (Declaring
errno)
This compliant solution demonstrates the correct way to declare
errno by including the header
<errno.h>:
#include <errno.h>
C-conforming
implementations are
required to declare
errno in
<errno.h>, although some historic implementations failed to
do so.
Risk Assessment
Accessing objects or functions underlying the specific macros enumerated in this rule is undefined behavior.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| MSC38-C | Low | Unlikely | Medium | P2 | L3 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C | DCL37-C. Do not declare or define a reserved identifier | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
| ISO/IEC 9899:2011 | 7.1.4, "Use of Library Functions" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
predefined_macro_added_as_object |
Do not treat a predefined identifier as an object if it might only be implemented as a macro. |
None |
False |
predefined_macro_expanded_in_object |
Do not treat a predefined identifier as an object if it might only be implemented as a macro. |
None |
False |
predefined_macro_used_as_object |
Do not treat a predefined identifier as an object if it might only be implemented as a macro. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
macros¶
macros : set[bauhaus.analysis.config.MacroName] = {'assert', 'errno', 'math_errhandling', 'setjmp', 'va_arg', 'va_copy', 'va_end', 'va_start'}