 
                            Some C standard library functions are not guaranteed to be reentrant with respect to threads. 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, the library functions listed in the following table may contain data races when invoked by multiple threads.
| Functions | Remediation | 
|---|---|
| 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 Kstrtok_r()in POSIX | 
| strerror() | strerror_s()in C11 Annex Kstrerror_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 Ktmpnam_r()in POSIX | 
| mbrtoc16(),c16rtomb(),mbrtoc32(),c32rtomb() | Do not call with a null mbstate_t *argument | 
Section 2.9.1 of the 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 a multithreaded application 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, 7.24.6.2 [ISO/IEC 9899:2011], specifically states that strerror() is not required to avoid data races. 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.
#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 the file position: %s\n", errmsg);
  }
}
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. 
Compliant Solution (Annex K, 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:
#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[BUFFERSIZE];
    if (strerror_s(errmsg, BUFFERSIZE, errno) != 0) {
      /* Handle error */
    }
    printf("Could not get the file position: %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 strerror_r() function, which has the same functionality as strerror() but guarantees thread safety:
#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[BUFFERSIZE];
    if (strerror_r(errno, errmsg, BUFFERSIZE) != 0) {
      /* Handle error */
    }
    printf("Could not get the file position: %s\n", errmsg);
  }
}
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, which is the default when an application is compiled as required by POSIX (that is, by defining _POSIX_C_SOURCE or _XOPEN_SOURCE appropriately). The strerror_r() manual page lists versions that are available on 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 a denial-of-service attack.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level | 
|---|---|---|---|---|---|
| CON33-C | Medium | Probable | 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 | 25.10 | Supported, but no explicit checker | |
| CodeSonar | 9.1p0 | BADFUNC.RANDOM.RAND | Use of  | 
| Compass/ROSE | 
 | 
 | A module written in Compass/ROSE can detect violations of this rule | 
| LDRA tool suite | 9.7.1 | 44 S | Partially Implemented | 
| Parasoft C/C++test | 9.5 | SECURITY-25 | |
| PRQA QA-C++ | 4.4 | 5021 | 
Related Guidelines
Bibliography
| [IEEE Std 1003.1:2013] | Section 2.9.1, "Thread Safety" | 
| [ISO/IEC 9899:2011] | Subclause 7.24.6.2, "The  | 
| [Open Group 1997b] | Section 10.12, "Thread-Safe POSIX.1 and C-Language Functions" | 


