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 |