メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C: Rule POS48-C

別の POSIX スレッドのミューテックスをロック解除または破棄しない

説明

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

ルール定義

別の POSIX スレッドのミューテックスをロック解除または破棄しないようにします。1

Polyspace 実装

ルール チェッカーは、"ロックされたミューテックスの破棄" をチェックします。

すべて展開する

問題

ロックされたミューテックスの破棄は、ミューテックスがロックされた後 (そのロックが解除される前に) タスクがミューテックスを破棄すると発生します。このロックと破棄は、同じタスクで行われることも、異なるタスクで行われることもあります。

リスク

ミューテックスは、共有変数を同時アクセスから保護するためにロックされます。ミューテックスがロックされた状態で破棄される場合、この保護が適用されません。

修正方法

この欠陥を修正するには、ミューテックスのロックを解除した後のみ破棄します。適切な設計は、以下のとおりです。

  • ミューテックスを使用するスレッドを作成する "前" にミューテックスを初期化します。

  • 作成したスレッドに加わった "後" にミューテックスを破棄します。

[結果の詳細] ペインに、ミューテックスのロックと破棄の 2 つのイベントと、そのイベントを開始したタスクが表示されます。ソース コード内の対応する行に移動するには、そのイベントをクリックします。

例 - 異なるタスクでのロックと破棄


#include <pthread.h>

pthread_mutex_t lock1;
pthread_mutex_t lock2;
pthread_mutex_t lock3;

void t0 (void) {
  pthread_mutex_lock (&lock1);
  pthread_mutex_lock (&lock2);
  pthread_mutex_lock (&lock3);
  pthread_mutex_unlock (&lock2);
  pthread_mutex_unlock (&lock1);
  pthread_mutex_unlock (&lock3);
}

void t1 (void) {
  pthread_mutex_lock (&lock1);
  pthread_mutex_lock (&lock2);
  pthread_mutex_destroy (&lock3); //Noncompliant
  pthread_mutex_unlock (&lock2);
  pthread_mutex_unlock (&lock1);
}

この例では、タスク t0 がミューテックス lock3 をロックした後、タスク t1 がこれを破棄する可能性があります。このような破棄は、以下のような順序でイベントが発生した場合に発生します。

  1. t0lock3 をかけます。

  2. t0lock2 を解除します。

  3. t0lock1 を解除します。

  4. t1t0 によって解除されたロック lock1 をかけます。

  5. t1t0 によって解除されたロック lock2 をかけます。

  6. t1lock3 を破棄します。

説明を簡単にするために、この例では同時実行の自動検出と手動検出を組み合わせて使用します。タスク t0t1 は、オプションタスク (-entry-points)を使用して、手動でエントリ ポイントに指定されます。本ソフトウェアによって自動検出されるプリミティブ型 pthread_mutex_lockpthread_mutex_unlock を使用して、クリティカル セクションを実装します。実際のエントリ ポイントの指定 (スレッド作成) には、pthread_create などのプリミティブ型を使用します。次の例では、pthread_create を使用すると、この欠陥がどのように現れる可能性があるかを示します。

修正 - 破棄と同じクリティカル セクションにロックとロック解除のペアを一緒に配置

lock3 のロックと破棄は、lock1lock2 によって定められたクリティカル セクション内部で行われていますが、ロック解除が外部で行われています。考えられる 1 つの修正方法として、ミューテックスの破棄と同じクリティカル セクションにロックとロック解除のペアを配置します。以下のいずれかのクリティカル セクションを使用します。

  • lock1 のみによって定められるクリティカル セクション。

  • lock1lock2 によって定められるクリティカル セクション。

修正した以下のコードでは、ロックとロック解除のペアと破棄が lock1lock2 によって定められるクリティカル セクションに配置されます。t0lock1lock2 をかけたとき、t1 はこのロックの解除を命令 pthread_mutex_destroy (&lock3); が実行されるまで待たなければはなりません。したがって、t1 は、ロックされた状態のミューテックス lock3 を破棄できません。



#include <pthread.h>

pthread_mutex_t lock1;
pthread_mutex_t lock2;
pthread_mutex_t lock3;

void t0 (void) {
  pthread_mutex_lock (&lock1);
  pthread_mutex_lock (&lock2);

  pthread_mutex_lock (&lock3);
  pthread_mutex_unlock (&lock3);

  pthread_mutex_unlock (&lock2);
  pthread_mutex_unlock (&lock1);
}

void t1 (void) {
  pthread_mutex_lock (&lock1);
  pthread_mutex_lock (&lock2);

  pthread_mutex_destroy (&lock3);

  pthread_mutex_unlock (&lock2);
  pthread_mutex_unlock (&lock1);
}

例 - スレッドの開始ルーチンでのロックと破棄
#include <pthread.h>

/* Define globally accessible variables and a mutex */
#define NUMTHREADS 4
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
void atomic_operation(void);

void *do_create(void *arg) {
    /* Creation thread */
    pthread_mutex_init(&lock, NULL);
    pthread_exit((void*) 0);
}

void *do_work(void *arg) {
    /* Worker thread */
    pthread_mutex_lock (&lock);
    atomic_operation();
    pthread_mutex_unlock (&lock);
    pthread_exit((void*) 0);
}

void *do_destroy(void *arg) {
    /* Destruction thread */
    pthread_mutex_destroy(&lock); //Noncompliant
    pthread_exit((void*) 0);
}

int main (int argc, char *argv[]) {  
   int i;
   void *status;
   pthread_attr_t attr;

           
   /* Create threads */
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

   /* Thread that initializes mutex */
   pthread_create(&callThd[0], &attr, do_create, NULL);

   /* Threads that use mutex for atomic operation*/
   for(i=0; i<NUMTHREADS-1; i++) {
      pthread_create(&callThd[i], &attr, do_work, (void *)i);
   }

   /* Thread that destroys mutex */
   pthread_create(&callThd[NUMTHREADS -1], &attr, do_destroy, NULL);

   pthread_attr_destroy(&attr);

   /* Join threads */
   for(i=0; i<NUMTHREADS; i++) {
      pthread_join(callThd[i], &status);
   }

   pthread_exit(NULL);
}

この例では、4 つのスレッドが作成されています。スレッドは異なるアクションに割り当てられています。

  • 1 番目のスレッド callThd[0] がミューテックス lock を初期化します。

  • 2 番目スレッド callThd[1] と 3 番目のスレッド callThd[2] が、ミューテックス lock によって保護されるアトミック操作を実行します。

  • 4 番目のスレッド callThd[3] が、ミューテックス lock を破棄します。

スレッドは互いに割り込まれる可能性があります。したがって、2 番目または 3 番目のスレッドがミューテックスをロックした直後に、4 番目のスレッドがこれを破棄する可能性があります。

修正 - 開始ルーチンの外部でミューテックスを初期化および破壊

考えられる 1 つの修正方法として、スレッドの開始ルーチン外部の main 関数でミューテックスを初期化および破棄します。スレッドではアトミック操作のみを実行します。ミューテックスの初期化と破棄を行うスレッドは必要なくなるため、スレッドを 2 つ減らす必要があります。

#include <pthread.h>

/* Define globally accessible variables and a mutex */
#define NUMTHREADS 2
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
void atomic_operation(void);

void *do_work(void *arg) {
   pthread_mutex_lock (&lock);
   atomic_operation();
   pthread_mutex_unlock (&lock);
   pthread_exit((void*) 0);
}

int main (int argc, char *argv[]) {  
   int i;
   void *status;
   pthread_attr_t attr;

           
   /* Create threads */
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

   /* Initialize mutex */
   pthread_mutex_init(&lock, NULL);

   for(i=0; i<NUMTHREADS; i++) {
      pthread_create(&callThd[i], &attr, do_work, (void *)i);
   }

   pthread_attr_destroy(&attr);

   /* Join threads */
   for(i=0; i<NUMTHREADS; i++) {
      pthread_join(callThd[i], &status);
   }

   /* Destroy mutex */
   pthread_mutex_destroy(&lock); 
  
   pthread_exit(NULL);
}

修正 - 2 番目のミューテックスを使用してロックとロック解除のペアの保護と破棄

別の修正方法として、2 番目のミューテックスを使用してロックとロック解除のペアを破棄から保護します。修正した次のコードでは、ミューテックス lock2 を使用してこの保護を実現しています。2 番目のミューテックスは、スレッドの開始ルーチン外部の main 関数で初期化されています。

#include <pthread.h>

/* Define globally accessible variables and a mutex */
#define NUMTHREADS 4
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
pthread_mutex_t lock2;
void atomic_operation(void);

void *do_create(void *arg) {
    /* Creation thread */
    pthread_mutex_init(&lock, NULL);
    pthread_exit((void*) 0);
}

void *do_work(void *arg) {
    /* Worker thread */
    pthread_mutex_lock (&lock2);
    pthread_mutex_lock (&lock);
    atomic_operation();
    pthread_mutex_unlock (&lock);
    pthread_mutex_unlock (&lock2);
    pthread_exit((void*) 0);
}

void *do_destroy(void *arg) {
    /* Destruction thread */
    pthread_mutex_lock (&lock2);
    pthread_mutex_destroy(&lock);
    pthread_mutex_unlock (&lock2);
    pthread_exit((void*) 0);
}


int main (int argc, char *argv[]) {  
   int i;
   void *status;
   pthread_attr_t attr;

           
   /* Create threads */
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

   /* Initialize second mutex */
   pthread_mutex_init(&lock2, NULL);

   /* Thread that initializes first mutex */
   pthread_create(&callThd[0], &attr, do_create, NULL);

   /* Threads that use first mutex for atomic operation */
   /* The threads use second mutex to protect first from destruction in locked state*/
   for(i=0; i<NUMTHREADS-1; i++) {
      pthread_create(&callThd[i], &attr, do_work, (void *)i);
   }

   /* Thread that destroys first mutex */
   /* The thread uses the second mutex to prevent destruction of locked mutex */
   pthread_create(&callThd[NUMTHREADS -1], &attr, do_destroy, NULL);


   pthread_attr_destroy(&attr);

   /* Join threads */
   for(i=0; i<NUMTHREADS; i++) {
      pthread_join(callThd[i], &status);
   }

   /* Destroy second mutex */
   pthread_mutex_destroy(&lock2);

   pthread_exit(NULL);
}

チェック情報

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

バージョン履歴

R2019a で導入


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.