メインコンテンツ

CERT C: Rule POS53-C

条件変数の同時待機操作に複数のミューテックスを使用しない

説明

このチェッカーは、既定の Polyspace® as You Code 解析では非アクティブにされますPolyspace as You Code 解析で非アクティブにされるチェッカー (Polyspace Access)を参照してください

ルール定義

条件変数の同時待機操作に複数のミューテックスを使用しないようにします。1

Polyspace 実装

ルール チェッカーは、"複数のミューテックスで同じ条件変数が使用されています" をチェックします。

すべて展開する

問題

この問題は、複数のスレッドが複数のミューテックスを使用して同じ条件変数を同時に待機している場合に発生します。スレッドは、関数 pthread_cond_timedwait または pthread_cond_wait を呼び出すことによって条件変数を待機します。これらの関数は条件変数とロックされたミューテックスを引数として取得し、条件変数はスレッドが条件変数を待機しているミューテックスにバインドされます。

チェッカーは、スレッドのいずれかにおける pthread_cond_timedwait または pthread_cond_wait の使用にフラグを設定します。同じ条件変数を待機しているスレッドを確認、別のミューテックスを使用するには、[結果の詳細] ペインで [イベント] 列を参照してください。

リスク

スレッドがミューテックスを使用して条件変数を待機すると、条件変数がそのミューテックスにバインドされます。別のミューテックスを使用して同じ条件変数を待機している他のスレッドは、POSIX 標準で定義されていない動作です。

修正方法

スレッドが同時に同じ条件変数を待機している場合は pthread_cond_timedwait または pthread_cond_wait に同じミューテックス引数を使用するか、ミューテックスごとに別々の条件変数を使用します。

例 - 複数のミューテックスを使用した条件変数の同時待機
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define Thrd_return_t                    void *
#define __USE_XOPEN2K8



#define COUNT_LIMIT 5

static void fatal_error(void)
{
    exit(1);
}


pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_mutex_t mutex3;
pthread_cond_t cv;

int count1 = 0, count2 = 0, count3 = 0;
#define DELAY 8

Thrd_return_t waiter1(void* arg)
{
    int ret;
    while (count1 < COUNT_LIMIT) {
        if ((ret = pthread_mutex_lock(&mutex1)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex1)) != 0) { //Noncompliant
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count1 = %d\n", ++count1);
        if ((ret = pthread_mutex_unlock(&mutex1)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t waiter2(void* arg)
{
    int ret;
    while (count2 < COUNT_LIMIT) {
        if ((ret = pthread_mutex_lock(&mutex2)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex2)) != 0) { //Noncompliant
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count2 = %d\n", ++count2);
        if ((ret = pthread_mutex_unlock(&mutex2)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t signaler(void* arg)
{
    int ret;
    while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) {
        sleep(1);
        printf("signaling\n");
        if ((ret = pthread_cond_broadcast(&cv)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t waiter3(void* arg)
{
    int ret;
    while (count3 % COUNT_LIMIT != 0) {
        if ((ret = pthread_mutex_lock(&mutex3)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex3)) != 0) { //Noncompliant
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count3 = %d\n", ++count3);
        if ((ret = pthread_mutex_unlock(&mutex3)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

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

    pthread_mutexattr_t attr;

    if ((ret = pthread_mutexattr_init(&attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
        /* Handle error */
        fatal_error();
    }

    if ((ret = pthread_mutex_init(&mutex1, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutex_init(&mutex2, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutex_init(&mutex3, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_cond_init(&cv, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread1, NULL, &waiter1, NULL))) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread2, NULL, &waiter2, NULL))) {
        /* handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread3, NULL, &signaler, NULL))) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread1, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread2, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread3, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }

    while (1) { ; }

    return 0;
}

この例では、別々のミューテックスを使用して各変数 count を保護します。3 つすべての関数 waiter が別々のミューテックスを使用して同じ条件変数 cv を待機しているため、スレッドのいずれかの pthread_cond_wait に対する呼び出しが成功すると、他の 2 つのスレッドの呼び出しが未定義になります。

チェッカーは、関数がスレッド、エントリポイント、または割り込みによって直接的または間接的に呼び出されなくても、関数 waiter3 の欠陥を報告します。解析では、関数 waiter3 が、その関数アドレスを介してメイン プログラムによって呼び出された、または、ソース コードが見当たらない不明なスレッドによって呼び出されたと見なされます。

修正 — 同じ条件変数を待機するすべてのスレッドに同じミューテックスを使用する

1 つの修正方法として、同じ条件変数を待機するために使用される pthread_cond_wait に対するすべての呼び出しに同じミューテックス引数を渡します。

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define Thrd_return_t                    void *
#define __USE_XOPEN2K8



#define COUNT_LIMIT 5

static void fatal_error(void)
{
    exit(1);
}


pthread_mutex_t mutex;

pthread_cond_t cv;

int count1 = 0, count2 = 0, count3 = 0;
#define DELAY 8

Thrd_return_t waiter1(void* arg)
{
    int ret;
    while (count1 < COUNT_LIMIT) {
        if ((ret = pthread_mutex_lock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count1 = %d\n", ++count1);
        if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t waiter2(void* arg)
{
    int ret;
    while (count2 < COUNT_LIMIT) {
        if ((ret = pthread_mutex_lock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count2 = %d\n", ++count2);
        if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t signaler(void* arg)
{
    int ret;
    while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) {
        sleep(1);
        printf("signaling\n");
        if ((ret = pthread_cond_broadcast(&cv)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}

Thrd_return_t waiter3(void* arg)
{
    int ret;
    while (count3 % COUNT_LIMIT != 0) {
        if ((ret = pthread_mutex_lock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        if ((ret =
                 pthread_cond_wait(&cv, &mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
        sleep(random() % DELAY);
        printf("count3 = %d\n", ++count3);
        if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
            /* Handle error */
            fatal_error();
        }
    }
    return (Thrd_return_t)0;
}
/* 
void user_task(void)
{
    (void)waiter3(NULL);
} */

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

    pthread_mutexattr_t attr;

    if ((ret = pthread_mutexattr_init(&attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
        /* Handle error */
        fatal_error();
    }

    if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_cond_init(&cv, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread1, NULL, &waiter1, NULL))) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread2, NULL, &waiter2, NULL))) {
        /* handle error */
        fatal_error();
    }
    if ((ret = pthread_create(&thread3, NULL, &signaler, NULL))) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread1, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread2, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }
    if ((ret = pthread_join(thread3, NULL)) != 0) {
        /* Handle error */
        fatal_error();
    }

    while (1) { ; }

    return 0;
}

チェック情報

グループ: Rule 50.POSIX (POS)

バージョン履歴

R2020a で導入


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.