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

Compare with Current View Page History

« Previous Version 63 Next »

Mutexes are used to protect shared data structures being accessed concurrently. The thread that locks the mutex owns it, and the owning thread should be the only thread to unlock the mutex. If the mutex is destroyed while still in use, critical sections and shared data are no longer protected.

The C Standard, section 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 implies that destroying a mutex while a thread is waiting on it is undefined behavior.

Noncompliant Code Example

In this noncompliant code example, a race condition exists between a cleanup and a worker thread. The worker thread uses a mutex lock, while the cleanup thread destroys the lock, which it believes is no longer in use. If there is a heavy load on the system, the worker thread that held the lock can take longer than expected. If the lock is destroyed before the worker thread has completed modifying the shared data, the program may exhibit undefined behavior.

#include <threads.h>
 
mtx_t lock;
int data;

void* do_cleanup(void* arg) {
  mtx_destroy(&lock);
  data++;
  return (void*) data;
}
 
void* do_work(void* arg) {
  if (thrd_success != mtx_lock(&lock)) {
    /* Handle error */
  }
  data += (int) arg;
  if (thrd_success != mtx_unlock(&lock)) {
    /* Handle error */
  }
  return 0;
}

int main(void) {
  thrd_t cleanup_thread, worker_thread;
 
  if (thrd_success != mtx_init(&lock, mtx_plain)) {
    /* Handle error */
  }
  if (thrd_success != thrd_create(&worker_thread, do_work, (void *)1)) {
    /* Handle error */
  }
  if (thrd_success != thrd_create(&cleanup_thread, do_cleanup, NULL)) {
    /* Handle error */
  }
  if (thrd_success != thrd_join(&worker_thread, 0)) {
    /* Handle error */
  }
  if (thrd_success != thrd_join(&cleanup_thread, 0)) {
    /* Handle error */
  }
  return 0;
}

Compliant Solution

This compliant solution requires that there is no chance a mutex will be needed after it has been destroyed. As always, it is important to check for error conditions when locking the mutex.

#include <threads.h>
 
mutex_t lock;
int data;
 
extern void wait_for_all_threads_to_finish(void);

int cleanupAndFinish(void) {
  /* A user-written function that is application-dependent. */
  wait_for_all_threads_to_finish();
  mtx_destroy(&lock);
  data++;
  return data;
}

void worker(int value) {
  int result;
  if (thrd_success != mtx_lock(&lock)) {
    /* Handle error */
  }
  data += value;
  if (thrd_success != mtx_unlock(&lock)) {
    /* Handle error */
  }
}

Risk Assessment

The risks of ignoring mutex ownership are similar to the risks of not using mutexes at all, which can result in a violation of data integrity.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON31-C

medium

probable

high

P4

L3

Automated Detection

Tool

Version

Checker

Description

Fortify SCA

V. 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]Section 7.26.4.1, "The mtx_destroy Function"

 


  • No labels