diff --git a/content/4.sei-cert-c-coding-standard/03.rules/05.concurrency-con/05.con33-c.md b/content/4.sei-cert-c-coding-standard/03.rules/05.concurrency-con/05.con33-c.md
index aa20ee85..145c294a 100644
--- a/content/4.sei-cert-c-coding-standard/03.rules/05.concurrency-con/05.con33-c.md
+++ b/content/4.sei-cert-c-coding-standard/03.rules/05.concurrency-con/05.con33-c.md
@@ -27,9 +27,9 @@ According to the C Standard, the library functions listed in the following table
| ` tmpnam() ` | ` tmpnam_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](/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 ® ), 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.
@@ -44,7 +44,7 @@ An [implementation](/sei-cert-c-coding-standard/back-matter/bb-definitions#BB.De
#include
#include
#include
-
+
void f(FILE *fp) {
fpos_t pos;
errno = 0;
@@ -70,7 +70,7 @@ This compliant solution uses the POSIX `strerror_r()` function, which has the sa
#include
enum { BUFFERSIZE = 64 };
-
+
void f(FILE *fp) {
fpos_t pos;
errno = 0;
@@ -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
+#include
+#include
+
+
+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()`)
+
+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
+#include
+#include
+
+
+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
+#include
+#include
+
+
+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) .
@@ -107,7 +270,7 @@ Search for [vulnerabilities](/sei-cert-c-coding-standard/back-matter/bb-definiti
| Astrée | 25.10
| **bad-function-use** | Partially checked + soundly supported |
| Axivion Suite | 7.12.0
| **CertC-CON33** | |
| CodeSonar | 9.2p0
| **CONCURRENCY.C_ATOMIC.INIT**
**BADFUNC.RANDOM.RAND**
**BADFUNC.TEMP.TMPNAM**
**BADFUNC.TTYNAME** | Inappropriate C Atomic Initialization
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 |
+| Compass/ROSE | | | A module written in Compass/ROSE can detect violations of this rule |
| Cppcheck Premium | 24.11.0
| **premium-cert-con33-c** | |
| Helix QAC | 2025.2
| **C5037**
**C++5021**
**DF4976, DF4977** | |
| Klocwork | 2025.2
| **CERT.CONC.LIB_FUNC_USE** | |
diff --git a/content/4.sei-cert-c-coding-standard/04.back-matter/4.cc-undefined-behavior.md b/content/4.sei-cert-c-coding-standard/04.back-matter/4.cc-undefined-behavior.md
index 384c9fd0..dadec353 100644
--- a/content/4.sei-cert-c-coding-standard/04.back-matter/4.cc-undefined-behavior.md
+++ b/content/4.sei-cert-c-coding-standard/04.back-matter/4.cc-undefined-behavior.md
@@ -207,7 +207,7 @@ According to the C Standard, Annex J, J.2 \[ [ISO/IEC 9899:2024](/sei-cert-c-cod
| 196 | ❌ | 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 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 | ⚠️ | 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 sequence of calls of the strtok function is made from different threads (7.26.5.9). | |
+| 199 | | 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 | ⚠️ | 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 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 | ⚠️ | 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). | |
@@ -231,7 +231,6 @@ According to the C Standard, Annex J, J.2 \[ [ISO/IEC 9899:2024](/sei-cert-c-cod
| 220 | ⚠️ | 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 | ⚠️ | 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 |