You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 78 Next »

Mutexes are used to protect shared data structures being concurrently accessed. If a mutex is destroyed while still in use, critical sections and shared data are no longer protected.

The C Standard, subclause 7.26.4.1, paragraph 2 [ISO/IEC 9899:2011], states:

The mtx_destroy function releases any resources used by the mutex pointed to by mtx. No threads can be blocked waiting for the mutex pointed to by mtx.

This sugests that destroying a mutex while a thread is waiting on it is implicit undefined behavior.

Noncompliant Code Example

This noncompliant code example creates several threads which each invoke the do_work() function, passing a unique number as an id. The do_work() function initiailzes the lock mutex if the argument is 0 and destroys the mutex if the the argument is max_threads - 1. In all other cases, the do_work() function simply provides normal processing. Each thread except the final cleanup thread increments the atomic completed variable when it is finished.

Unfortunately, this code contains several race conditions, allowing the mutex to be destroyed before it is unlocked.  Additionally, there is no guarantee that lock will have been initialized before it is passed to mtx_lock(). Both conditions yield undefined behavior.

#include <stdatomics.h>
#include <threads.h>
 
mtx_t lock;
atomic_int completed; /* Atomic so multiple threads can modify safely */
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 small 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.

#include <stdatomics.h>
#include <threads.h>
 
mtx_t lock;
atomic_int completed; /* Atomic so multiple threads can increment safely */
enum { max_threads = 5 }; 

int do_work(void *arg) {
  if (thrd_success != mtx_lock(&lock)) {
    /* Handle error */
  }

  /* Access data protected by the small lock */

  atomic_fetch_add(&completed, 1);
  if (thrd_success != mtx_unlock(&lock)) {
    /* Handle error */
  }

  return 0;
}

int main(void) {
  thrd_t threads[max_threads];
  atomic_store(&completed, 0);
  
  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, &i)) {
      /* 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 invaild control flow and data corruption.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON31-C

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

Related Vulnerabilities

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

Related Guidelines

MITRE CWECWE-667, Insufficient locking

Bibliography

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

 


  • No labels