Versions Compared

Key

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

...

In this noncompliant code example, mutex1 protects count1 and mutex2 protects count2. A race condition exists between the waiter1 and waiter2 threads because they use the same condition variable with different mutexes. If both threads attempt to call pthread_cond_wait() at the same time, one thread will succeed and the other thread will invoke undefined behavior.

Code Block
bgColor#FFcccc
langc
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>

pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_mutexattr_t attr;
pthread_cond_t cv;

void *waiter1();
void *waiter2();
void *signaler();

int count1 = 0, count2 = 0;
#define COUNT_LIMIT 5

int main() {
  int ret;
  pthread_t thread1, thread2, thread3;

  if ((ret = pthread_mutexattr_init( &attr)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_mutex_init( &mutex1, &attr)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_mutex_init( &mutex2, &attr)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_cond_init( &cv, NULL)) != 0) {
    /* handle error */
  }

  if ((ret = pthread_create( &thread1, NULL, &waiter1, NULL))) {
    /* handleHandle error */
  }

  if ((ret = pthread_create( &thread2, NULL, &waiter2, NULL))) {
    /* handle error */
  }

  if ((ret = pthread_create( &thread3, NULL, &signaler, NULL))) {
    /* handleHandle error */
  }

  if ((ret = pthread_join( thread1, NULL)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_join( thread2, NULL)) != 0) {
    /* handleHandle error */
  }

  if ((ret = pthread_join( thread3, NULL)) != 0) {
    /* handleHandle error */
  }

  return 0;
}


void *waiter1() {
  int ret;
  while (count1 < COUNT_LIMIT) {
    if ((ret = pthread_mutex_lock(&mutex1)) != 0) {
      /* handleHandle error */
    }

    if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) {
      /* handleHandle error */
    }

    printf("count1 = %d\n", ++count1);

    if ((ret = pthread_mutex_unlock(&mutex1)) != 0) {
      /* handleHandle error */
    }
  }

  return NULL;
}

void *waiter2() {
  int ret;
  while (count2 < COUNT_LIMIT) {
    if ((ret = pthread_mutex_lock(&mutex2)) != 0) {
      /* handleHandle error */
    }

    if ((ret = pthread_cond_wait(&cv, &mutex2)) != 0) {
      /* handleHandle error */
    }

    printf("count2 = %d\n", ++count2);

    if ((ret = pthread_mutex_unlock(&mutex2)) != 0) {
      /* handleHandle error */
    }
  }

  return NULL;
}

void *signaler() {
  int ret;
  while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) {
    sleep(1);
    printf("signaling\n");
    if ((ret = pthread_cond_signal(&cv)) != 0) {
      /* handleHandle error */
    }
  }

  return NULL;
}

...

pthread_cond_wait() returns EINVAL if it is called when another thread is waiting on the condition variable with a different mutex. This approach is arguably better because it forces the coder to fix the problem instead of allowing reliance on undefined behavior.

...

This problem can be solved either by always using the same mutex whenever a particular condition variable is used or by using separate condition variables, depending on how the code is expected to work. Here we use This compliant solution uses the €œsame-mutex€ solution.:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <pthread.h>
 
pthread_mutex_t mutex1; /* initializedInitialized as PTHREAD_MUTEX_ERRORCHECK */
pthread_cond_t cv;
int count1 = 0, count2 = 0;
#define COUNT_LIMIT 5

void *waiter1() {
  int ret;
  while (count1 < COUNT_LIMIT) {
    if ((ret = pthread_mutex_lock(&mutex1)) != 0) {
      /* handleHandle error */
    }

    if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) {
      /* handleHandle error */
    }

    printf("count1 = %d\n", ++count1);

    if ((ret = pthread_mutex_unlock(&mutex1)) != 0) {
      /* handleHandle error */
    }
  }

  return NULL;
}

void *waiter2() {
  int ret;
  while (count2 < COUNT_LIMIT) {
    if ((ret = pthread_mutex_lock(&mutex1)) != 0) {
      /* handleHandle error */
    }

    if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) {
      /* handleHandle error */
    }

    printf("count2 = %d\n", ++count2);

    if ((ret = pthread_mutex_unlock(&mutex1)) != 0) {
      /* handleHandle error */
    }
  }

  return NULL;
}

...

The severity is medium because improperly accessing shared data could lead to data integrity violation. Likelihood is probable because in such an implementation, an error code would not be returned, and remediation cost is high because detection and correction of this problem are both manual.

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

POS53-C

Medium

Probable

Yes

No

P8

L2

Automated Detection

Tool

Version

Checker

Description

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C1769

C++1769


Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C

medium

probable

high

P4

L3

...

-POS53-a

Do not use more than one mutex for concurrent waiting operations on a condition variable

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule POS53-CChecks for multiple mutexes used with same conditional variable (rule fully covered)


Bibliography


...

Image Modified Image Modified Image Modified