...
This statement implies that destroying a mutex while a thread is waiting on it is undefined behavior.
Noncompliant Code Example
This noncompliant code example creates several threads that each invoke the do_work() function, passing a unique number as an ID. The do_work() function initializes the lock mutex if the argument is 0 and destroys the mutex if the argument is max_threads - 1. In all other cases, the do_work() function provides normal processing. Each thread, except the final cleanup thread, increments the atomic completed variable when it is finished.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h>
#include <threads.h>
mtx_t lock;
/* Atomic so multiple threads can modify safely */
atomic_int completed = ATOMIC_VAR_INIT(0);
enum { max_threads = 5 };
int do_work(void *arg) {
int *i = (int *)arg;
if (*i == 0) { /* Creation thread */
if (thrd_success != mtx_init(&lock, mtx_plain)) {
/* Handle error */
}
atomic_store(&completed, 1);
} else if (*i < max_threads - 1) { /* Worker thread */
if (thrd_success != mtx_lock(&lock)) {
/* Handle error */
}
/* Access data protected by the lock */
atomic_fetch_add(&completed, 1);
if (thrd_success != mtx_unlock(&lock)) {
/* Handle error */
}
} else { /* Destruction thread */
mtx_destroy(&lock);
}
return 0;
}
int main(void) {
thrd_t threads[max_threads];
for (size_t i = 0; i < max_threads; i++) {
if (thrd_success != thrd_create(&threads[i], do_work, &i)) {
/* Handle error */
}
}
for (size_t i = 0; i < max_threads; i++) {
if (thrd_success != thrd_join(threads[i], 0)) {
/* Handle error */
}
}
return 0;
}
|
Compliant Solution
This compliant solution eliminates the race conditions by initializing the mutex in main() before creating the threads and by destroying the mutex in main() after joining the threads:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h>
#include <threads.h>
mtx_t lock;
/* Atomic so multiple threads can increment safely */
atomic_int completed = ATOMIC_VAR_INIT(0);
enum { max_threads = 5 };
int do_work(void *dummy) {
if (thrd_success != mtx_lock(&lock)) {
/* Handle error */
}
/* Access data protected by the lock */
atomic_fetch_add(&completed, 1);
if (thrd_success != mtx_unlock(&lock)) {
/* Handle error */
}
return 0;
}
int main(void) {
thrd_t threads[max_threads];
if (thrd_success != mtx_init(&lock, mtx_plain)) {
/* Handle error */
}
for (size_t i = 0; i < max_threads; i++) {
if (thrd_success != thrd_create(&threads[i], do_work, NULL)) {
/* Handle error */
}
}
for (size_t i = 0; i < max_threads; i++) {
if (thrd_success != thrd_join(threads[i], 0)) {
/* Handle error */
}
}
mtx_destroy(&lock);
return 0;
}
|
Risk Assessment
Destroying a mutex while it is locked may result in invalid control flow and data corruption.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
CON31CON50-CCPP | Medium | Probable | High | P4 | L3 |
Automated Detection
Tool | Version | Checker | Description |
|---|---|---|---|
5.0 |
| Can detect violations of this rule with CERT C Rule Pack | |
| Parasoft C/C++test | 9.5 | BD-RES-FREE, BD-RES-INVFREE |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
| [ISO/IEC 9899:2011] | 7.26.4.1, "The mtx_destroy Function" |
...