Versions Compared

Key

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

...

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
bgColor#ffcccc
langc
#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
bgColor#ccccff
langc
#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

Fortify SCA

5.0

 

Can detect violations of this rule with CERT C Rule Pack

Parasoft C/C++test9.5BD-RES-FREE, BD-RES-INVFREE 

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

MITRE CWECWE-667, Improper Locking

Bibliography

[ISO/IEC 9899:2011]7.26.4.1, "The mtx_destroy Function"

...