メインコンテンツ

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

CERT C: Rule POS49-C

When data must be accessed by multiple threads, provide a mutex and guarantee no adjacent data is also accessed

説明

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

ルール定義

複数のスレッドでデータにアクセスしなければならない場合は、ミューテックスを提供し、隣接データがアクセスされないことも保証します。1

Polyspace 実装

ルール チェッカーは、"隣接するビット フィールドでのデータ レース" をチェックします。

すべて展開する

問題

隣接するビット フィールドでのデータ レースは以下の場合に発生します。

  • 複数のタスクが、同じ構造体の一部であるビット フィールドに対して非保護操作を実行した。

    たとえば、次の型の変数で、あるタスクがフィールド errorFlag1 を操作し、別のタスクがフィールド errorFlag2 を操作した場合です。

    struct errorFlags {
       unsigned int errorFlag1 : 1;
       unsigned int errorFlag2 : 1;
       ...
    }
    操作は相互にアトミックではないと仮定します。つまり、ある操作が完了してから次の操作が開始されるようにするための保護メカニズムが実装されていません。

  • 非保護操作のうち少なくとも 1 は書き込み操作である。

リスク

同じ構造体の一部である隣接するビット フィールドは、同じメモリ位置にある 1 バイトに保存される可能性があります。ビット フィールドを含むすべての変数に対する読み取り操作または書き込み操作は、一度に 1 バイトまたは 1 ワードで発生します。1 つのバイト内の特定のビットのみを変更するには、次のようなステップが続けて発生します。

  1. そのバイトが RAM に読み込まれます。

  2. 特定のビットだけが意図した値に変更され、残りのビットは変更されないようにマスクが作成されます。

  3. RAM 内のバイトのコピーとマスク間でビット OR 演算が実行されます。

  4. 特定のビットが変更されたバイトが RAM からコピーされます。

2 つの異なるビット フィールドがアクセスされた場合は、各ビット フィールドに対してこの 4 つのステップを実行する必要があります。アクセスが保護されていない場合は、1 つのビット フィールドに対する 4 つすべてのステップが完了しなくても、他のビット フィールドに対する 4 つのステップが開始される可能性があります。その結果、1 つのビット フィールドの変更によって、隣接するビット フィールドの変更が取り消される可能性があります。たとえば、errorFlag1errorFlag2 の変更は次の順で発生します。1 とマークされたステップは errorFlag1 に関連し、2 とマークされたステップは errorFlag2 に関連します。

1a.変更されていない errorFlag1errorFlag2 の両方を含むバイトが errorFlag1 を変更する目的で RAM にコピーされます。

1b.errorFlag1 だけを変更するマスクがこのコピーとビット単位 OR されます。

2a.errorFlag2 を変更する目的で、変更されていない errorFlag1errorFlag2 の両方を含むバイトが 2 回 RAM にコピーされます。

2b.errorFlag2 だけを変更するマスクがこの 2 つ目のコピーとビット単位 OR されます。

1c.errorFlag1 が変更されたバージョンがコピーされます。このバージョンの errorFlag2 は変更されていません。

2c. errorFlag2 が変更されたバージョンがコピーされます。このバージョンの errorFlag1 は変更されておらず、以前の変更が上書きされます。

修正方法

この欠陥を修正するには、クリティカル セクション、時間的排他、または別の手段を使用して同じ構造体の一部であるビット フィールドに対する操作を保護します。マルチタスキング コードでの共有変数の保護を参照してください。

再利用できる既存の保護を特定するには、結果に関連付けられている表とグラフを確認します。表では競合する呼び出しの各ペアが示されます。[アクセス保護] 列には、その呼び出しについての既存の保護が表示されます。競合につながる関数呼び出しの順序を確認するには、 アイコンをクリックします。以下の例を参照してください。

例 - 複数の POSIX スレッドからのグローバル変数に対する非保護操作
#include <stdlib.h>
#include <pthread.h>
#define thread_success 0

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12; //Noncompliant

void* task1 (void* arg) {
    InterruptConfigbitsProc12.IOFlag = 0;
    //Additional code
}

void* task2 (void* arg) {
    InterruptConfigbitsProc12.SetupFlag = 0;
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}

この例では、ID が thread1thread2 のスレッドが、同じ構造化変数 InterruptConfigbitsProc12 に属している別々のビット フィールドの IOFlagSetupFlag にアクセスします。

修正 - クリティカル セクションを使用

1 つの修正方法として、1 つのクリティカル セクションでビット フィールドのアクセスをラップします。クリティカル セクションは、ロック関数およびロック解除関数に対する呼び出しの間に配置されます。この修正では、クリティカル セクションが関数の pthread_mutex_lockpthread_mutex_unlock に対する呼び出しの間に配置されます。

#include <stdlib.h>
#include <pthread.h>
#define thread_success 0
#define lock_success 0

pthread_mutex_t lock;

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void* task1 (void* arg) {
    if( lock_success != pthread_mutex_lock(&lock)) {
        //Handle error
    }
    InterruptConfigbitsProc12.IOFlag = 0;
    if( lock_success != pthread_mutex_unlock(&lock)) {
        //Handle error
    }
    //Additional code
}

void* task2 (void* arg) {
    if( lock_success != pthread_mutex_lock(&lock)) {
        //Handle error
    }
    InterruptConfigbitsProc12.SetupFlag = 0;
    if( lock_success != pthread_mutex_unlock(&lock)) {
        //Handle error
    }
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}
修正 – サイズ 0 のビット フィールドを挿入

同時にアクセス可能な 2 つの隣接するビット フィールドの間に非ビット フィールド メンバーまたはサイズ 0 の無名のビット フィールド メンバーを入力できます。非ビット フィールド メンバーまたはサイズ 0 のビット フィールド メンバーは、後続のビット フィールドが新しいメモリ位置から始まるようにします。この修正例では、サイズ 0 のビット フィールド メンバーが、IOFlagSetupFlag が異なるメモリ位置に保存されるようにします。

#include <stdlib.h>
#include <pthread.h>
#define thread_success 0

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int : 0;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void* task1 (void* arg) {
    InterruptConfigbitsProc12.IOFlag = 0;
    //Additional code
}

void* task2 (void* arg) {
    InterruptConfigbitsProc12.SetupFlag = 0;
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}

チェック情報

グループ: 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.