Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Some 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, the following library functions listed in the following table may contain data races when invoked by multiple threads:.

Library Functions Subject to Data Races
FunctionsRemediationAPIRecommendation
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 APIs 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  the POSIX Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 [IEEE Std 1003.1:2013] augments extends the list of functions that are not required to be thread-safe.

Noncompliant Code Example

Consider In this noncompliant code example, the function f() is called from within 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, subclause 7.24.6.2 [ISO/IEC 9899:2011], specifically states that strerror() is not required to avoid data races. Conventionally, it could rely on An implementation could write the error string into a static array that maps error numbers to error stringsand return a pointer to it, and that array might be accessible and modifiable by other threads.

Code Block
bgColor#FFCCCC
langc
#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: because of %s\n",
           errmsg);
  }
}

Note that 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

...

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.:

Code Block
bgColor#ccccff
langc
#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 because of: %s\n",
           errmsg);
  }
}

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

...

This compliant solution uses the POSIX strerror_r() function, which has the same functionality as strerror() but guarantees thread - safety:

Code Block
bgColor#ccccff
langc
#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 because of %s\n",
           errmsg);
  }
}

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, which you will get if you compile your application 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 Thestrerror_r() manual page to see which version(s) lists versions that are available on your 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.

...

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

Subclause 7.24.6.2, "The strerror Function" 

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

...