CertC-MSC38

Do not treat a predefined identifier as an object if it might only be implemented as a macro

Required inputs: IR

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.

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"
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/miscellaneous-msc/msc38-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

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

macros

macros : set[bauhaus.analysis.config.MacroName] = {'assert', 'errno', 'math_errhandling', 'setjmp', 'va_arg', 'va_copy', 'va_end', 'va_start'}

Set of names of forbidden predefined macros.