メインコンテンツ

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

CERT C: Rule CON33-C

Avoid race conditions when using library functions

説明

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

ルール定義

Avoid race conditions when using library functions.1

Polyspace 実装

ルール チェッカーは、"標準ライブラリ関数呼び出しでデータ レースが発生しました" をチェックします。

チェッカーの拡張

ルール チェッカーが問題を検出できるようにするには、スレッドの作成と共有変数の保護のために Polyspace が認識する同時実行プリミティブの 1 つをコードで使用する必要があります。あるいは、プロジェクト構成でタスク、割り込み、クリティカル セクション、およびその他のマルチタスク オプションを明示的に指定する必要があります。マルチタスキングを参照してください。

すべて展開する

問題

標準ライブラリ関数呼び出しでデータ レースが発生しましたは以下の場合に発生します。

  • 複数のタスクから同じ標準ライブラリ関数を呼び出す。

    たとえば、複数のタスクから strerror 関数を呼び出します。

  • 呼び出しが共通の保護を使って保護されていない。

    たとえば、呼び出しが同じクリティカル セクションによって保護されていません。

この欠陥によってフラグが付けられた関数は、再呼び出し可能であることは保証されません。前の呼び出しが実行を完了する前に、割り込まれたり、安全に再度呼び出されることができる場合、関数は再呼び出し可能です。関数が再呼び出し可能ではない場合、複数のタスクからその関数を保護なしで呼び出すと、同時実行の問題の原因となる可能性があります。フラグが付けられた関数のリストについては、次を参照してください。CON33-C:Avoid race conditions when using library functions

この欠陥を検出するには、解析前にマルチタスキング オプションを指定しなくてはなりません。[構成] ペインで [マルチタスキング] を選択してこれらのオプションを指定します。詳細は、Polyspace マルチタスキング解析の手動設定を参照してください。

リスク

この欠陥によってフラグが付けられた関数は、その実装でグローバル変数や静的変数が使用される可能性があるため、再呼び出し可能ではありません。複数のタスクから保護なしでその関数を呼び出すと、一方のタスクからの関数呼び出しが別のタスクからの呼び出しに干渉する可能性があります。その関数の 2 つの呼び出しがグローバル変数や静的変数に同時にアクセスし、予期しない結果が生じる可能性があります。

この呼び出しが、異常終了、サービス拒否攻撃、データの整合性違反など、より深刻なセキュリティの脆弱性の原因となることもあります。

修正方法

この欠陥を修正するには、以下のいずれかを行います。

  • 標準ライブラリ関数の再呼び出し可能なバージョンが存在する場合はそちらを使用します。

    たとえば、strerror() ではなく、strerror_r() または strerror_s() を使用します。この欠陥によってフラグが付けられた関数の代替方法については、「CON33-C」を参照してください。

  • 共通のクリティカル セクションまたは時間的に排他を使用して、その関数呼び出しを保護します。

    クリティカル セクション詳細 (-critical-section-begin -critical-section-end)および時間的に排他なタスク (-temporal-exclusions-file)を参照してください。

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

例 - 標準ライブラリ関数に対する複数のタスクからの保護されない呼び出し

#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char *errmsg = strerror(errno); //Noncompliant
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    func(fptr1);
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     func(fptr2);
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}

この例では、マルチタスクの動作をエミュレートするため、以下のオプションを指定しなければなりません。

オプション仕様
マルチタスクを手動で構成
タスク (-entry-points)

task1

task2

task3

クリティカル セクション詳細 (-critical-section-begin -critical-section-end)開始ルーチン終了ルーチン
begin_critical_sectionend_critical_section

コマンド ラインでは以下を使用できます。

 polyspace-bug-finder
   -entry-points task1,task2,task3
   -critical-section-begin begin_critical_section:cs1
   -critical-section-end end_critical_section:cs1

この例では、タスク task1task2 および task3 が関数 func を呼び出します。関数 func は再呼び出し可能ではない標準ライブラリ関数 strerror を呼び出します。

task3 はクリティカル セクションの内部で func を呼び出しますが、他のタスクは同じクリティカル セクションを使用していません。task3 のクリティカル セクション内の操作は、他のタスクでの操作と相互に排他的ではありません。

これらの 3 つのタスクは、共通の保護を使用しないで再呼び出し可能ではない標準ライブラリ関数を呼び出しています。結果の詳細で、競合する関数呼び出しの各ペアが表示されます。

A snapshot of the Result Details pane showing calls to a standard library function from several pairs of threads or tasks. The Access Protections column shows whether the calls are protected from concurrent access.

アイコンをクリックすると、標準ライブラリ関数呼び出しへのエントリ ポイントから始まる関数呼び出し順序が表示されます。task3 から始まる呼び出しがクリティカル セクションに含まれていることも確認できます。[アクセス保護] エントリでは、クリティカル セクションを開始、終了するロック関数とロック解除関数が表示されます。この例では、関数 begin_critical_sectionend_critical_section が表示されます。

A pictorial view of calls to a standard library function from a pair of threads or tasks.

修正 - 標準ライブラリ関数の再呼び出し可能なバージョンを使用

考えられる 1 つの修正方法として、標準ライブラリ関数 strerror の再呼び出し可能なバージョンを使用します。POSIX® バージョンの strerror_r を使用できます。これには同じ機能がありますが、スレッド セーフ性が保証されます。


#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);
enum { BUFFERSIZE = 64 };

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char errmsg[BUFFERSIZE];
    if (strerror_r(errno, errmsg, BUFFERSIZE) != 0) {
      /* Handle error */
    }
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    func(fptr1);
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     func(fptr2);
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}
修正 — クリティカル セクション内に関数呼び出しを配置

考えられる 1 つの修正方法として、strerror の呼び出しをクリティカル セクション内に配置します。クリティカル セクションは複数の方法で実装できます。

たとえば、中間関数 func の呼び出しを 3 つのタスクの同じクリティカル セクション内に配置できます。task1 がクリティカル セクションに入るとき、他のタスクは task1 がクリティカル セクションを離れるまで入ることができません。func の呼び出し、つまり 3 つのタスクからの strerror の呼び出しは互いに干渉する可能性はありません。

クリティカル セクションを実装するには、3 つのそれぞれのタスク内で begin_critical_sectionend_critical_section の呼び出しの間に func を呼び出します。


#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char *errmsg = strerror(errno);
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    begin_critical_section();
    func(fptr1);
    end_critical_section();
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     begin_critical_section();
     func(fptr2);
     end_critical_section();
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}

修正 — タスクを時間的に排他にする

別の修正方法として、タスク task1task2 および task3 を時間的に排他にします。時間的に排他なタスクは同時に実行することはできません。

[構成] ペインで、以下の追加オプションを指定します。

コマンド ラインでは以下を使用できます。

 polyspace-bug-finder
     -temporal-exclusions-file "C:\exclusions_file.txt"
ここで、ファイル C:\exclusions_file.txt には以下の行があります。
task1 task2 task3

チェック情報

グループ: Rule 14.同時実行 (CON)

バージョン履歴

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.