CertC-PRE13

Use the Standard predefined macros to test for versions and features

Required inputs: IR

The C Standard defines a set of predefined macros (see subclause 6.10.8) to help the user determine if the implementation being used is a conforming implementation, and if so, to which version of the C Standard it conforms. These macros can also help the user to determine which of the standard features are implemented.

The following tables list these macros and indicate in which version of the C Standard they were introduced. The following macros are required:

Macro Name C90 C99 C11
__STDC__
__STDC_HOSTED__
__STDC_VERSION__1
__DATE__
__FILE__
__LINE__
__TIME__
  1) __STDC_VERSION__ was introduced by an Amendment to C90, this version of the C Standard is commonly call C94 

 The following are optional environment macros:

Macro Name C90 C99 C11
__STDC_ISO_10646__
__STDC_MB_MIGHT_NEQ_WC__
__STDC_UTF_16__

__STDC_UTF_32__

  The following are optional feature macros:

Macro Name C90 C99 C11
__STDC_ANALYZABLE__

__STDC_IEC_559__
__STDC_IEC_559_COMPLEX__
__STDC_LIB_EXT1__ 

__STDC_NO_ATOMICS__

__STDC_NO_COMPLEX__

__STDC_NO_THREADS__

__STDC_NO_VLA__ 

 The following is optional and is defined by the user:

Macro Name C90 C99 C11
__STDC_WANT_LIB_EXT1__ 

 
Noncompliant Code Example (Checking Value of Predefined Macro)

C Standard predefined macros should never be tested for a value before the macro is tested for definition, as shown in this noncompliant code example:

#include <stdio.h>

int main(void) {
  #if (__STDC__ == 1)
    printf("Implementation is ISO-conforming.\n");
  #else
    printf("Implementation is not ISO-conforming.\n");
  #endif
  /* ... */

  return 0;
}
Compliant Solution (Testing for Definition of Macro)

In this compliant solution, the definition of the predefined macro  __STDC__ is tested before the value of the macro is tested:

#include <stdio.h>

int main(void) {
  #if defined(__STDC__)
    #if (__STDC__ == 1)
      printf("Implementation is ISO-conforming.\n");
    #else
      printf("Implementation is not ISO-conforming.\n");
    #endif
  #else   /* !defined(__STDC__) */
    printf("__STDC__ is not defined.\n");
  #endif
  /* ... */
  return 0;
}
Compliant Solution (Test for Optional Feature)

This compliant solution tests to see if the C11 predefined macro  __STDC_ANALYZABLE__ is defined and what value the implementation has given the macro:

#include <stdio.h>
 
int main(void) {
  #if defined (__STDC__)
    #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)  /* C11 */
      #if defined(__STDC_ANALYZABLE__)
        #if (__STDC_ANALYZABLE__ == 1)
           printf("Compiler conforms to Annex L (Analyzability).\n");
        #else
           printf("Compiler does not support Annex L (Analyzability).\n");
        #endif
      #else
        printf("__STDC_ANALYZABLE__ is not defined.\n");
      #endif
    #else
      printf("Compiler not C11.\n");
    #endif
  #else
    printf("Compiler not Standard C.\n");
  #endif
 
  return 0;
}
Compliant Solution (Optional Language Features)

This compliant solution checks for the C11 optional language features in Annex K. If Annex K is supported by the implementation, the functions defined in Annex K are used; if Annex K is not supported, then the standard library functions are used. (See  DCL09-C. Declare functions that return errno with a return type of errno_t .)

#if defined(__STDC_LIB_EXT1__)
  #if (__STDC_LIB_EXT1__ >= 201112L)
    #define USE_EXT1 1
    #define __STDC_WANT_LIB_EXT1__ 1 /* Want the ext1 functions */
  #endif
#endif
 
#include <string.h>
#include <stdlib.h>
 
int main(void) {
  char source_msg[] = "This is a test.";
  char *msg = malloc(sizeof(source_msg) + 1);
 
  if (msg != NULL) {
    #if defined(USE_EXT1)
      strcpy_s(msg, sizeof msg, source_msg);
    #else
      strcpy(msg, source_msg);
    #endif
  } 
  else {
    return EXIT_FAILURE;
  }
  return 0;
}
Compliant Solution (Optional Language Features)

The previous compliant solution comes close to violating PRE09-C. Do not replace secure functions with deprecated or obsolescent functions, and would if a function-like macro were defined which called either strcpy_s() or strcpy() depending on if USE_EXT1 were defined.  This compliant solution solves the problem by including a custom library that implements the optional language feature, which in this case is the Safe C Library available from SourceForge.

#if defined(__STDC_LIB_EXT1__)
  #if (__STDC_LIB_EXT1__ >= 201112L)
    #define USE_EXT1 1
    #define __STDC_WANT_LIB_EXT1__ 1 /* Want the ext1 functions */
  #endif
#endif
 
#include <string.h>
#include <stdlib.h>

#if !defined(USE_EXT1)
  #include "safe_str_lib.h"
#endif
  
int main(void) {
  char source_msg[] = "This is a test.";
  char *msg = malloc(sizeof(source_msg) + 1);
 
  if (msg != NULL) {
    strcpy_s(msg, sizeof msg, source_msg);
  } 
  else {
    return EXIT_FAILURE;
  }
  return 0;
}
Risk Assessment

Not testing for language features or the version of the implementation being used can lead to unexpected or undefined program behavior.

Rule Severity Likelihood Remediation Cost Priority Level
PRE13-C Low Probable Low P6 L2
Related Guidelines
ISO/IEC TR 24772:2013 Pre-processor Directives [NMP]
ISO/IEC 9899:2011

6.10.8, "Predefined macro names"

K.3.7.1, "Copying 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/recommendations/preprocessor-pre/pre13-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

macro_value_test_without_definition_check

Check that a macro is defined before testing its value in an #if

None

False

Options

macros

macros

Type: set[bauhaus.analysis.config.MacroName]

Default: {'__DATE__', '__FILE__', '__LINE__', '__STDC_ANALYZABLE__', '__STDC_HOSTED__', '__STDC_IEC_559_COMPLEX__', '__STDC_IEC_559__', '__STDC_ISO_10646__', '__STDC_LIB_EXT1__', '__STDC_MB_MIGHT_NEQ_WC__', '__STDC_NO_ATOMICS__', '__STDC_NO_COMPLEX__', '__STDC_NO_THREADS__', '__STDC_NO_VLA__', '__STDC_UTF_16__', '__STDC_UTF_32__', '__STDC_VERSION__', '__STDC_WANT_LIB_EXT1__', '__STDC__', '__TIME__'}

Optional set of macros to restrict the check to.