Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ According to the C Standard, the library functions listed in the following table
| ` tmpnam() ` | ` tmpnam_r() ` in POSIX |
| ` mbrtoc16() ` , ` c16rtomb() ` , <br> ` mbrtoc32() ` , ` c32rtomb() ` | Do not call with a null ` mbstate_t * ` argument |

Section 2.9.1 of the *Portable Operating System Interface (POSIX <sup>®</sup> ), Base Specifications, Issue 7* \[ [IEEE Std 1003.1:2013](/sei-cert-c-coding-standard/back-matter/aa-bibliography#AA.Bibliography-IEEEStd1003.1-2013) \] extends the list of functions that are not required to be thread-safe.
Section 2.9.1 of the *Portable Operating System Interface (POSIX <sup>®</sup> ), Base Specifications, Issue 7* \[ [IEEE Std 1003.1:2013](/sei-cert-c-coding-standard/back-matter/aa-bibliography#AA.Bibliography-IEEEStd1003.1-2013) \] extends the list of functions that are not required to be thread-safe.

## Noncompliant Code Example
## Noncompliant Code Example (`strerror()`)

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.

Expand All @@ -44,7 +44,7 @@ An [implementation](/sei-cert-c-coding-standard/back-matter/bb-definitions#BB.De
#include <errno.h>
#include <stdio.h>
#include <string.h>
 
void f(FILE *fp) {
fpos_t pos;
errno = 0;
Expand All @@ -70,7 +70,7 @@ This compliant solution uses the POSIX `strerror_r()` function, which has the sa
#include <string.h>

enum { BUFFERSIZE = 64 };
 
void f(FILE *fp) {
fpos_t pos;
errno = 0;
Expand All @@ -88,6 +88,169 @@ void f(FILE *fp) {

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.

## Noncompliant Code Example (`strtok()`)

Starting a sequence of calls to the `strtok()` function from one thread and making a subsequent call in the same sequence from a different thread is [undefined behavior 199](/sei-cert-c-coding-standard/back-matter/cc-undefined-behavior#CC.UndefinedBehavior-ub_199), according to ISO C section 7.26.5.9.

::code-block{quality="bad"}
``` c
#include <string.h>
#include <threads.h>
#include <stdlib.h>


int child(void *p) {
char *t = strtok(NULL, "#,"); // Undefined Behavior

// Work with token...

return t ? (unsigned char) t[0] : -1;
}

int main(int argc, char** argv) {
if (argc < 2) {
// handle error
abort();
}
char *str = argv[1]; // Example: "?a???b,,,#c"

char *t = strtok(str, "?");
thrd_t thr;
if (thrd_success != thrd_create(&thr, child, 0)) {
// Handle Error
}

t = strtok(NULL, ",");

// Work with token...

int retval;
if (thrd_success != thrd_join(thr, &retval)) {
// Handle Error
}

return 0;
}
```
::

## Noncompliant Code Example (POSIX, `strtok_r()`)
Comment thread
sei-dsvoboda marked this conversation as resolved.

This noncompliant code example the POSIX `strtok_r()` function, which is reentrant. It relies on no static variables, always tokenizing the string in its 3rd `saveptr` argument, so it complies with this rule. However, by permitting a [data race](/sei-cert-c-coding-standard/back-matter/bb-definitions#BB.Definitions-datarace) on `str` via `saveptr`, this code violates [CON43-C. Do not allow data races in multithreaded code](/sei-cert-c-coding-standard/rules/concurrency-con/con43-c).

::code-block{quality="bad"}
``` c
#include <string.h>
#include <threads.h>
#include <stdlib.h>


int child(void *p) {
char *saveptr = p;
char *t = strtok_r(NULL, "#,", &saveptr);

// Work with token...

return t ? (unsigned char) t[0] : -1;
}

int main(int argc, char** argv) {
if (argc < 2) {
// handle error
abort();
}
char *str = argv[1]; // Example: "?a???b,,,#c"

char *saveptr = NULL;
char *t = strtok_r(str, "?", &saveptr);
thrd_t thr;
if (thrd_success != thrd_create(&thr, child, &saveptr)) {
// Handle Error
}

t = strtok_r(NULL, ",", &saveptr);

// Work with token...

int retval;
if (thrd_success != thrd_join(thr, &retval)) {
// Handle Error
}

return 0;
}
```
::

## Compliant Solution (POSIX, `strtok_r()` )

This compliant solution uses a mutex to prevent data races. There is still a race condition as to which thread invokes `strtok_r()`, but there is no data race on `saveptr` or `str`, as proscribed by [CON43-C. Do not allow data races in multithreaded code](/sei-cert-c-coding-standard/rules/concurrency-con/con43-c).

::code-block{quality="good"}
``` c
#include <string.h>
Comment thread
sei-dsvoboda marked this conversation as resolved.
#include <threads.h>
#include <stdlib.h>


static mtx_t lock;

int child(void *p) {
char* saveptr = p;

if (mtx_lock(&lock) == thrd_error) {
return -1; /* Indicate error to caller */
}
char *t = strtok_r(NULL, "#,", &saveptr);
if (mtx_unlock(&lock) == thrd_error) {
return -1; /* Indicate error to caller */
}

// Work with token...

return t ? (unsigned char) t[0] : -1;
}

int main(int argc, char** argv) {
if (argc < 2) {
// handle error
abort();
}
char *str = argv[1]; // Example: "?a???b,,,#c"

char *saveptr = NULL;
char *t = strtok_r(str, "?", &saveptr);

if(mtx_init(&lock, mtx_plain) == thrd_error) {
/* Handle error */
}

thrd_t thr;
if (thrd_success != thrd_create(&thr, child, saveptr)) {
// Handle Error
}

if (mtx_lock(&lock) == thrd_error) {
return -1; /* Indicate error to caller */
}
t = strtok_r(NULL, ",", &saveptr);
if (mtx_unlock(&lock) == thrd_error) {
return -1; /* Indicate error to caller */
}

// Work with token...

int retval;
if (thrd_success != thrd_join(thr, &retval)) {
// Handle Error
}

return 0;
}
```
::


## Risk Assessment

Race conditions caused by multiple threads invoking the same library function can lead to [abnormal termination](/sei-cert-c-coding-standard/back-matter/bb-definitions#BB.Definitions-abnormaltermination) of the application, data integrity violations, or a [denial-of-service attack](/sei-cert-c-coding-standard/back-matter/bb-definitions#BB.Definitions-denial-of-service) .
Expand All @@ -107,7 +270,7 @@ Search for [vulnerabilities](/sei-cert-c-coding-standard/back-matter/bb-definiti
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/astree">Astrée</a> | <div class="content-wrapper">25.10</div> | **bad-function-use** | Partially checked + soundly supported |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/axivion-suite">Axivion Suite</a> | <div class="content-wrapper">7.12.0</div> | **CertC-CON33** | |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/codesonar">CodeSonar</a> | <div class="content-wrapper">9.2p0</div> | **CONCURRENCY.C_ATOMIC.INIT** <br /> **BADFUNC.RANDOM.RAND** <br /> **BADFUNC.TEMP.TMPNAM** <br /> **BADFUNC.TTYNAME** | Inappropriate C Atomic Initialization <br /> Use of <code>rand</code> (includes check for uses of <code>srand()</code>) <br /> Use of <code>tmpnam</code> (includes check for uses of <code>tmpnam_r()</code>) <br /> Use of <code>ttyname</code> |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/rose">Compass/ROSE</a> | | | A module written in Compass/ROSE can detect violations of this rule |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/rose">Compass/ROSE</a> | | | A module written in Compass/ROSE can detect violations of this rule |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/cppcheck-premium">Cppcheck Premium</a> | <div class="content-wrapper">24.11.0</div> | **premium-cert-con33-c** | |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/helix-qac">Helix QAC</a> | <div class="content-wrapper">2025.2</div> | **C5037** <br /> **C++5021** <br /> **DF4976, DF4977** | |
| <a href="/sei-cert-c-coding-standard/back-matter/ee-analyzers/klocwork">Klocwork</a> | <div class="content-wrapper">2025.2</div> | **CERT.CONC.LIB_FUNC_USE** | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ According to the C Standard, Annex J, J.2 \[ [ISO/IEC 9899:2024](/sei-cert-c-cod
| 196 <a id="CC.UndefinedBehavior-ub_196"></a> | ❌ | A string or wide string utility function is instructed to access an array beyond the end of an object (7.26.1, 7.31.4). | |
| 197 <a id="CC.UndefinedBehavior-ub_197"></a> | ℹ️ | A string or wide string utility function is called with an invalid pointer argument, even if the length is zero (7.26.1, 7.31.4). | |
| 198 <a id="CC.UndefinedBehavior-ub_198"></a> | ⚠️ | The contents of the destination array are used after a call to the `strxfrm` , `strftime` , `wcsxfrm` , or `wcsftime` function in which the specified length was too small to hold the entire null-terminated result (7.26.4.5, 7.29.3.5, 7.31.4.4.4, 7.31.5.1). | |
| 199 <a id="CC.UndefinedBehavior-ub_199"></a> | | A sequence of calls of the strtok function is made from different threads (7.26.5.9). | |
| 199 <a id="CC.UndefinedBehavior-ub_199"></a> | | A sequence of calls of the strtok function is made from different threads (7.26.5.9). | [CON33-C](/sei-cert-c-coding-standard/rules/concurrency-con/con33-c) |
| 200 <a id="CC.UndefinedBehavior-ub_200"></a> | ⚠️ | The first argument in the very first call to the `strtok` or `wcstok` is a null pointer (7.26.5.9, 7.31.4.5.8). | |
| 201 <a id="CC.UndefinedBehavior-ub_201"></a> | | A pointer returned by the strerror function is used after a subsequent call to the function, or after the calling thread has exited (7.26.6.3). | [ENV34-C](/sei-cert-c-coding-standard/rules/environment-env/env34-c) |
| 202 <a id="CC.UndefinedBehavior-ub_202"></a> | ⚠️ | The type of an argument to a type-generic macro is not compatible with the type of the corresponding parameter of the selected function (7.27). | |
Expand All @@ -231,7 +231,6 @@ According to the C Standard, Annex J, J.2 \[ [ISO/IEC 9899:2024](/sei-cert-c-cod
| 220 <a id="CC.UndefinedBehavior-ub_220"></a> | ⚠️ | The `iswctype` function is called using a different `LC_CTYPE` category from the one in effect for the call to the `wctype` function that returned the description (7.32.2.2.1). | |
| 221 <a id="CC.UndefinedBehavior-ub_221"></a> | ⚠️ | The `towctrans` function is called using a different `LC_CTYPE` category from the one in effect for the call to the `wctrans` function that returned the description (7.32.3.2.1). | |


Graphical symbols used in the preceding table:

| Symbol | C11 Classification |
Expand Down