...
The C Standard, 7.1.4 paragraph 1, [ISO/IEC
...
9899:2024] 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 later in the next subclause 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. 220) The use of #undef to remove any macro definition will also ensure that an actual function is referred to.
220)This means that an implementation is required to 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 109, 113, 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, a programmer working with a known <assert.h> is attempting to subvert the standard assert
functionality so that assertions are always made, regardless of whether ndebug
is setthe 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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <assert.h> #undef assert typedef void (*handler_type)(int); void myassert(execute_handler(handler_type handler, int evalue) { __assert13(__FILE__, __LINE__, __assert_function__, e); } |
...
handler(value);
}
void func(int e) {
execute_handler(&(assert), e < 0);
} |
Compliant Solution (assert
)
In this compliant solution, the programmer does not #undef a standard macro, and explicitly handles any release-build assertion checks. assert()
macro is wrapped in a helper function, removing the undefined behavior:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <assert.h> typedef void myassert(int e) { assert(e); /* other verification ... */ } (*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:
Code Block | ||||
---|---|---|---|---|
| ||||
extern int errno;
|
Compliant Solution (Declaring errno
)
This compliant solution demonstrates the correct way to declare errno
by including the header <errno.h>
:
Code Block | ||||
---|---|---|---|---|
| ||||
#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 function functions underlying these macros does not produce defined behavior, which may lead to incorrect or unexpected program the specific macros enumerated in this rule is undefined behavior.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MSC38-C |
Low |
Unlikely |
Medium | P2 | L3 |
References
Wiki Markup |
---|
\[[ISO/IEC 9899:1999|AA. References#ISO/IEC 9899-1999]\], all sections indicated by the [undefined behavior items |CC. Undefined Behavior] noted above. |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| Supported, but no explicit checker | |||||||
CodeSonar |
| BADMACRO.STDARG_H | Use of <stdarg.h> Feature | ||||||
Cppcheck Premium |
| premium-cert-msc38-c | Fully implemented | ||||||
Helix QAC |
| C3437, C3475 C++3127, C++5039 | |||||||
Parasoft C/C++test |
| CERT_C-MSC38-a | A function-like macro shall not be invoked without all of its arguments | ||||||
| CERT C: Rule MSC38-C | Checks for predefined macro used as an object (rule fully covered) | |||||||
RuleChecker |
| Supported, but no explicit checker |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
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:2024 | 7.1.4, "Use of Library Functions" |
...
koders.com, <assert.h> source.