Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated references from C11->C23

Some functions in the C standard library functions are not guaranteed to be reentrant with respect to threads. Some functions (Functions such as strtok() and asctime()) return a pointer to the result stored in function-allocated memory on a per-process basis. Other functions ( such as rand()) store state information in function-allocated memory on a per-process basis. Multiple threads invoking the same function can cause concurrency problems, which often result in abnormal behavior and can cause more serious vulnerabilities, such as abnormal termination, denial-of-service attack, and data integrity violations.

According to the C Standard [ISO/IEC 9899:2011], the following library functions are not required to avoid data races:

  • rand()
  • getenv()
  • strtok()
  • strerror()
  • asctime()
  • ctime()

listed in the following table may contain data races when invoked by multiple threads.

FunctionsRemediation
rand(), srand()MSC30-C. Do not use the rand() function for generating pseudorandom numbers
getenv(), getenv_s()ENV34-C. Do not store pointers returned by certain functions
strtok()strtok_s() in C11 Annex K
strtok_r() in POSIX
strerror()strerror_s() in C11 Annex K
strerror_r() in POSIX
asctime(), ctime(),
localtime(), gmtime()
asctime_s(), ctime_s(), localtime_s(), gmtime_s() in C11 Annex K
setlocale()Protect multithreaded access to locale-specific functions with a mutex
ATOMIC_VAR_INIT, atomic_init()Do not attempt to initialize an atomic variable from multiple threads
tmpnam()tmpnam_s() in C11 Annex K
tmpnam_r() in POSIX
mbrtoc16(), c16rtomb(),
mbrtoc32(), c32rtomb()
Do not call with a null mbstate_t * argument 

Section 2.9.1 of the System Interfaces volume of POSIX.1-2008 has a much longer list Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 [IEEE Std 1003.1:2013] extends the list of functions that are not required to be thread-safe.

Noncompliant Code Example

...

In this noncompliant code example, the function f() is called from within Consider a multithreaded application that but encounters an error while calling a system function. The strerror() function returns a human-readable error string given an error number.

The C Standard, section 7.2426.6.23 paragraph 3 [ISO/IEC 9899:2024], specifically states that strerror() is not required to avoid data races. Conventionally, it could rely on a static array that maps error numbers to error strings.

The strerror function is not required to avoid data races with other calls to the strerror function.

An implementation could write the error string into a static array and return a pointer to it, and that array might be accessible and modifiable by other threads. (This code is specific to POSIX because fopen() is not guaranteed to set errno if an error occurs in C99 or C11.)

Code Block
bgColor#FFCCCC
langc
errno = 0;
FILE* fd = fopen( filename, "r");
if (fd == NULL#include <errno.h>
#include <stdio.h>
#include <string.h>
 
void f(FILE *fp) {
  fpos_t pos;
  errno = 0;

  if (0 != fgetpos(fp, &pos)) {
    char * errmsg = strerror(errno);
    printf("Could not get openthe file becauseposition: of %s\n", errmsg);
  }
}

Note that this This code first sets errno to 0 to comply with ERR30-C. Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure.

...

Take care when reading errno

Compliant Solution (Annex K

...

strerror_

...

s()) 

This noncompliant code example compliant solution uses the POSIX strerror_rs() function from Annex K of the C Standard, which has the same functionality as strerror() but guarantees thread-safety.:

Code Block
bgColor#FFCCCC#ccccff
langc
errno = 0;
FILE* fd = fopen( filename, "r");
if (fd == NULL) {
#define __STDC_WANT_LIB_EXT1__ 1
#include <errno.h>
#include <stdio.h>
#include <string.h>
 
enum { BUFFERSIZE = 64 };
void f(FILE *fp) {
  fpos_t pos;
  errno = 0;

  if (0 != fgetpos(fp, &pos)) {
    char errmsg[BUFSIZBUFFERSIZE];
    if (strerror_rs(errnoerrmsg, errmsgBUFFERSIZE, BUFSIZerrno) != 0) {
      /* handleHandle error */
    }
    printf("Could not get openthe file becauseposition: of %s\n", errmsg);
  }
}

Because Annex K is optional, strerror_s() may not be available in all implementations. 

Compliant Solution (POSIX, strerror_r())

This compliant solution uses the POSIX While this code prevents a race window from being exploited within the strerror_r() function itself, the fact that errno is a static variable means there is still a race window between the fopen() call and the beginning of the strerror_r() call function, in which another thread could modify errno.

Compliant Solution (C99 mutex) 

This compliant solution adds a mutex to protect the access of errno by multiple threads.which has the same functionality as strerror() but guarantees thread safety:

Code Block
bgColor#ccccff
langc
static pthread_mutex_t errno_mutex;
int result;
if ((result = pthread_mutex_lock(&errno_mutex)) == 0#include <errno.h>
#include <stdio.h>
#include <string.h>

enum { BUFFERSIZE = 64 };
 
void f(FILE *fp) {
  /* Handle error */
}
fpos_t pos;
  errno = 0;

FILE* fd   if (0 != fopenfgetpos( filenamefp, "r");
if (fd == NULL&pos)) {
    char errmsg[BUFSIZBUFFERSIZE];
    if (strerror_r(errno, errmsg, BUFSIZBUFFERSIZE) != 0) {
      /* handleHandle error */
    }
    printf("Could not openget the file because ofposition: %s\n", errmsg);
}
if ((result = pthread_mutex_unlock(&errno_mutex)) == 0) {
  /* Handle error */ }
}

Note that Linux provides two versions of strerror_r(), known as the XSI-compliant version and the GNU-specific version. This compliant solution assumes the XSI-compliant version. You can get the XSI-compliant version if you compile applications in the way POSIX requires , which is the default when an application is compiled as required by POSIX (that is, by defining _POSIX_C_SOURCE or _XOPEN_SOURCE appropriately). Check your The strerror_r() manual page to see which version(s) lists versions that are available on your system.

Compliant Solution (C11 strerror_s()

This compliant solution uses the strerror_s() function from Annex K of the C Standard, which has the same functionality as strerror() but guarantees thread-safety. Furthermore, in C11, errno is a thread-local variable, so there is no race condition between when it is initialized and read by strerror_s().

Code Block
bgColor#ccccff
langc
errno = 0;
FILE* fd = fopen( filename, "r");
if (fd == NULL) {
  char errmsg[BUFSIZ];
  if (strerror_s(errno, errmsg, BUFSIZ) != 0) {
    /* handle error */
  }
  printf("Could not open file because of %s\n", errmsg);
}

Note that because of the optional nature of Annex K, strerror_s() may not be available in all implementations. a particular system.

Risk Assessment

Race conditions caused by multiple threads invoking the same library function can lead to abnormal termination of the application, data integrity violations, or denial-of-service attack.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON33-C

medium

Medium

probable

Probable

high

High

P4

L3

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V

Supported, but no explicit checker
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.RANDOM.RAND
BADFUNC.TEMP.TMPNAM
BADFUNC.TTYNAME

Use of rand (includes check for uses of srand())
Use of tmpnam (includes check for uses of tmpnam_r())
Use of ttyname

Compass/ROSE
 

 



A module written in Compass/ROSE can detect violations of this rule

Cppcheck Premium

Include Page
Cppcheck Premium_V
Cppcheck Premium_V

premium-cert-con33-cFully implemented
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C5037

C++5021

DF4976, DF4977


Klocwork
Include Page
Klocwork_V
Klocwork_V

CERT.CONC.LIB_FUNC_USE


LDRA tool suite
 
Include Page
LDRA_V
LDRA_V
44 SPartially Implemented
Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-CON33-a

Avoid using thread-unsafe functions

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

586

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule CON33-CChecks for data race through standard library function call (rule fully covered)

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C

...

...

...

...

11CWE-3302017-06-28: CERT: Partial overlap
CWE 2.11CWE-3772017-06-28: CERT: Partial overlap
CWE 2.11CWE-6762017-05-18: CERT: Rule subset of CWE

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-330 and CON33-C

Independent( MSC30-C, MSC32-C, CON33-C)

Intersection( CWE-330, CON33-C) =


  • Use of rand() or srand() from multiple threads, introducing a race condition.


CWE-330 – CON33-C =


  • Use of rand() or srand() without introducing race conditions



  • Use of other dangerous functions


CON33-C – CWE-330 =


  • Use of other global functions (besides rand() and srand()) introducing race conditions


CWE-377 and CON33-C

Intersection( CWE-377, CON33-C) =


  • Use of tmpnam() from multiple threads, introducing a race condition.


CWE-377 – CON33-C =


  • Insecure usage of tmpnam() without introducing race conditions



  • Insecure usage of other functions for creating temporary files (see CERT recommendation FIO21-C for details)


CON33-C – CWE-377 =


  • Use of other global functions (besides tmpnam()) introducing race conditions


CWE-676 and CON33-C


  • Independent( ENV33-C, CON33-C, STR31-C, EXP33-C, MSC30-C, ERR34-C)



  • CON33-C lists standard C library functions that manipulate global data (e.g., locale()), that can be dangerous to use in a multithreaded context.



  • CWE-676 = Union( CON33-C, list) where list =



  • Invocation of the following functions without introducing a race condition:



  • rand(), srand(, getenv(), getenv_s(), strtok(), strerror(), asctime(), ctime(), localtime(), gmtime(), setlocale(), ATOMIC_VAR_INIT, atomic_init(), tmpnam(), mbrtoc16(), c16rtomb(), mbrtoc32(), c32rtomb()



  • Invocation of other dangerous functions




Bibliography

[IEEE Std 1003.1:2013]Section 2.9.1, "Thread Safety"
[ISO/IEC 9899:2024]

Subclause 7.26.6.3, "The strerror Function" 

[Open Group 1997b]Section 10.12, "Thread-Safe POSIX.1 and C-Language Functions"

ISO/IEC 9899:2011 Section 7.22.2.1, "The rand function," Section 7.22.4.6, "The getenv function," Section 7.24.5.8, "The strtok function," Section 7.24.6.2, "The strerror function," Section 7.27.3.1, "The asctime function," Section 7.27.3.2, "The ctime function"

Sources

...


...

Image Modified Image Modified Image Modified