|
| 1 | +# MSC38-C: Do not treat a predefined identifier as an object if it might only be implemented as a macro |
| 2 | + |
| 3 | +This query implements the CERT-C rule MSC38-C: |
| 4 | + |
| 5 | +> Do not treat a predefined identifier as an object if it might only be implemented as a macro |
| 6 | +
|
| 7 | + |
| 8 | +## Description |
| 9 | + |
| 10 | +The C Standard, 7.1.4 paragraph 1, \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\] states |
| 11 | + |
| 12 | +> 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.<sup>185</sup> |
| 13 | +
|
| 14 | + |
| 15 | +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. |
| 16 | + |
| 17 | +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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The macros are `assert`, `errno`, `math_errhandling`, `setjmp`, `va_arg`, `va_copy`, `va_end`, and `va_start`. These cases are described by [undefined behaviors](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) [110](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_110), [114](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_114), [122](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_122), [124](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_124), and [138](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_138). Programmers must not suppress these macros to access the underlying object or function. |
| 18 | + |
| 19 | +## Noncompliant Code Example (assert) |
| 20 | + |
| 21 | +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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). |
| 22 | + |
| 23 | +```cpp |
| 24 | +#include <assert.h> |
| 25 | + |
| 26 | +typedef void (*handler_type)(int); |
| 27 | + |
| 28 | +void execute_handler(handler_type handler, int value) { |
| 29 | + handler(value); |
| 30 | +} |
| 31 | + |
| 32 | +void func(int e) { |
| 33 | + execute_handler(&(assert), e < 0); |
| 34 | +} |
| 35 | +``` |
| 36 | +
|
| 37 | +## Compliant Solution (assert) |
| 38 | +
|
| 39 | +In this compliant solution, the `assert()` macro is wrapped in a helper function, removing the [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): |
| 40 | +
|
| 41 | +```cpp |
| 42 | +#include <assert.h> |
| 43 | + |
| 44 | +typedef void (*handler_type)(int); |
| 45 | + |
| 46 | +void execute_handler(handler_type handler, int value) { |
| 47 | + handler(value); |
| 48 | +} |
| 49 | + |
| 50 | +static void assert_handler(int value) { |
| 51 | + assert(value); |
| 52 | +} |
| 53 | + |
| 54 | +void func(int e) { |
| 55 | + execute_handler(&assert_handler, e < 0); |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +## Noncompliant Code Example (Redefining errno) |
| 60 | + |
| 61 | +Legacy code is apt to include an incorrect declaration, such as the following in this noncompliant code example: |
| 62 | + |
| 63 | +```cpp |
| 64 | +extern int errno; |
| 65 | + |
| 66 | +``` |
| 67 | + |
| 68 | +## Compliant Solution (Declaring errno) |
| 69 | + |
| 70 | +This compliant solution demonstrates the correct way to declare `errno` by including the header `<errno.h>`: |
| 71 | + |
| 72 | +```cpp |
| 73 | +#include <errno.h> |
| 74 | + |
| 75 | +``` |
| 76 | +[C-conforming](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-conformingprogram) [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) are required to declare `errno` in `<errno.h>`, although some historic implementations failed to do so. |
| 77 | + |
| 78 | +## Risk Assessment |
| 79 | + |
| 80 | +Accessing objects or functions underlying the specific macros enumerated in this rule is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). |
| 81 | + |
| 82 | +<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> MSC38-C </td> <td> Low </td> <td> Unlikely </td> <td> Medium </td> <td> <strong>P2</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table> |
| 83 | + |
| 84 | + |
| 85 | +## Automated Detection |
| 86 | + |
| 87 | +<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> </td> <td> Supported, but no explicit checker </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>BADMACRO.STDARG_H</strong> </td> <td> Use of <stdarg.h> Feature </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C3437, C3475</strong> <strong>C++3127, C++5039</strong> </td> <td> </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-MSC38-a</strong> </td> <td> A function-like macro shall not be invoked without all of its arguments </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule MSC38-C </a> </td> <td> Checks for predefined macro used as an object (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>3437, 3475</strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> </td> <td> Supported, but no explicit checker </td> </tr> </tbody> </table> |
| 88 | + |
| 89 | + |
| 90 | +## Related Vulnerabilities |
| 91 | + |
| 92 | +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MSC38-C). |
| 93 | + |
| 94 | +## Related Guidelines |
| 95 | + |
| 96 | +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) |
| 97 | + |
| 98 | +<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> CERT C </a> </td> <td> <a> DCL37-C. Do not declare or define a reserved identifier </a> </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> </tbody> </table> |
| 99 | + |
| 100 | + |
| 101 | +## Bibliography |
| 102 | + |
| 103 | +<table> <tbody> <tr> <td> <a> ISO/IEC 9899:2011 </a> </td> <td> 7.1.4, "Use of Library Functions" </td> </tr> </tbody> </table> |
| 104 | + |
| 105 | + |
| 106 | +## Implementation notes |
| 107 | + |
| 108 | +This query reports locations corresponding to both redefinitions of those standard library macros as well as locations where the identifiers used for accesses. |
| 109 | + |
| 110 | +## References |
| 111 | + |
| 112 | +* CERT-C: [MSC38-C: Do not treat a predefined identifier as an object if it might only be implemented as a macro](https://wiki.sei.cmu.edu/confluence/display/c) |
0 commit comments