CERT.CONC.COND_MULTIPLE_MUTEX
Do not use more than one mutex for concurrent waiting operations on a condition variable.
The CERT.CONC.COND_MULTIPLE_MUTEX checker detects waits on the same POSIX condition variable with different mutexes. While a thread is waiting on a condition variable, every concurrent wait on that condition variable must use the same mutex.
Vulnerability and risk
pthread_cond_wait() and pthread_cond_timedwait() unlock the supplied mutex while the thread waits, then relock that mutex before returning. If different threads wait on the same condition variable with different mutexes, the behavior is undefined and the code can resume with the wrong mutex locked.
Mitigation and prevention
Associate exactly one mutex with each condition variable and use that mutex in every waiting operation on it. If separate mutexes are required, use separate condition variables instead of sharing one condition variable across those waits.
Vulnerable code example
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static int shared_data = 0;
void *waiter1(void *arg)
{
pthread_mutex_lock(&mutex1);
while (shared_data == 0) {
pthread_cond_wait(&cv, &mutex1);
}
pthread_mutex_unlock(&mutex1);
return NULL;
}
void *waiter2(void *arg)
{
pthread_mutex_lock(&mutex2);
while (shared_data == 0) {
pthread_cond_wait(&cv, &mutex2); /* Noncompliant */
}
pthread_mutex_unlock(&mutex2);
return NULL;
}
In this noncompliant example, both threads wait on the same condition variable, but one thread uses mutex1 and the other uses mutex2. Because the condition variable is paired with more than one mutex, the second wait has undefined behavior.
Fixed code example
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static int shared_data = 0;
void *waiter1(void *arg)
{
pthread_mutex_lock(&mutex1);
while (shared_data == 0) {
pthread_cond_wait(&cv, &mutex1);
}
pthread_mutex_unlock(&mutex1);
return NULL;
}
void *waiter2(void *arg)
{
pthread_mutex_lock(&mutex1);
while (shared_data == 0) {
pthread_cond_wait(&cv, &mutex1);
}
pthread_mutex_unlock(&mutex1);
return NULL;
}
In the compliant solution, both threads use the same mutex with the condition variable. If separate mutexes are required, use separate condition variables instead of sharing one condition variable.