メインコンテンツ

CERT C: Rec.EXP12-C

関数から返された値を無視しない

説明

ルール定義

関数から返された値を無視しないようにします。1

Polyspace 実装

ルール チェッカーは、"要注意の関数の戻り値がチェックされていません" をチェックします。

すべて展開する

問題

要注意の関数の戻り値がチェックされていませんは、要注意の標準関数を呼び出すときに、以下を行うと発生します。

  • 戻り値を無視。

  • 戻り値の妥当性をテストすることなく出力または戻り値を使用。

Polyspace® は、次の場合にはこのルールに対する違反を報告しません。

  • & 演算子と ~ 演算子を使用したビット単位のマスキング演算で、関数の戻り値をチェックせずにその値を使用するが、後でマスクされた値をチェックする場合。たとえば、以下のコードでは、要注意の関数 bar() の戻り値 val がチェックされません。ただし、ビット単位のマスキング演算で val が使用されて、演算 masked_val の結果がチェックされるため、Polyspace は違反を報告しません。

    void foo(){
        int val  = bar() // Sensitive function - Compliant
        int masked_val = val & 0xff;
        if(masked_val){
            //...
        }
    }

  • 条件付きステートメントの各分岐で呼び出される要注意の関数の値を格納し、条件付きステートメントの最後でその値をチェックする場合。たとえば、以下のコードでは、要注意の関数 foo() および foo2() が、if-else ステートメントの 2 つの分岐でそれぞれ呼び出されます。いずれの戻り値も条件付きで return_value に格納されます。条件付きステートメントの後で return_value がチェックされるため、Polyspace は違反を報告しません。

    void func(){
        //...
        int volatile cond;
        int returned_val;
        if(cond){
            returned_val = foo();
        }else{
            returned_val = foo2();
        }
        if(returned_val){
            do_something();
        }
    }

この違反では、"要注意""重要で要注意" という 2 つのタイプの関数が考慮されます。

"要注意" の関数とは、以下に遭遇する可能性のある標準関数です。

  • システム リソースが使い尽くされた状態 (リソースを割り当てるときなど)

  • 権限またはアクセス許可が変更された状態

  • 外部ソースからのデータを読み取り、書き込みまたは変換する際にソースが汚染された状態

  • 既存の API があってもサポートされない機能

"重要で要注意" の関数とは、次の重要または脆弱なタスクのいずれかを実行する要注意関数です。

  • 権限の設定 (setuid など)

  • jail の作成 (chroot など)

  • プロセスの作成 (fork など)

  • スレッドの作成 (pthread_create など)

  • ミューテックスのロックまたはロック解除 (pthread_mutex_lock など)

  • メモリ セグメントのロックまたはロック解除 (mlock など)

リスク

要注意または重要で要注意のタスクを実行する関数の戻り値をチェックしない場合、プログラムが予期しない動作をする可能性があります。これらの関数のエラーはプログラム全体に伝播し、不適切な出力、セキュリティの脆弱性およびシステム障害の原因となる可能性があります。

修正方法

プログラムを続行する前に、"重要で要注意" の関数の戻り値をテストします。

"要注意の関数" の場合、関数を void にキャストすることで戻り値を明示的に無視することができます。Polyspace では、要注意の関数が void をキャストしている場合、欠陥とは見なされません。"重要で要注意の関数" の場合、さらに脆弱なタスクが実行されるため、この解決策は許容されません。

チェッカーの拡張

既定では、このルール チェッカーは要注意の関数や、重要で要注意の関数の使用を検出します。要注意の関数や、重要で要注意の関数をコードで指定するには、Datalog ファイルを使用します。Datalog ファイル内で、次のコンポーネントを含めます。

  • ソース ファイル models/interfaces/sensitive_function.dl 内の定義を含めます。

  • 関数を要注意として指定します。

    sensitive_function.is_sensitive("bar").

  • 関数を重要として指定します。

    sensitive_function.is_critical("foo").
    

  • 次のようにして、一部の関数を無視するように、このルール チェッカーを構成することもできます。

    • sensitive_function.ignore_all() — 既定の関数を無視するように、ルール チェッカーを構成します。

    • sensitive_function.ignore_standard($sensitive_function.std()) std で指定した特定のセットで要注意の関数と、重要で要注意の関数を無視するように、ルール チェッカーを構成します。std が取り得る値には、ISOPOSIXOPENSSLUNIXWINDOWS があります。

    • sensitive_function.ignore_function("func") — 関数 func を無視するように、ルール チェッカーを構成します。

Datalog ファイルを、オプション -code-behavior-specifications の入力として指定します。

例 — 要注意の関数の戻り値が無視される
#include <pthread.h>

void initialize() {
    pthread_attr_t attr;

    pthread_attr_init(&attr);  //Noncompliant
}

この例は、要注意関数 pthread_attr_init の呼び出しを示します。pthread_attr_init の戻り値が無視され、欠陥の原因になっています。

修正 - 関数を (void) にキャスト

考えられる 1 つの修正方法として、関数を void にキャストします。この修正では Polyspace およびレビュー担当者に、要注意の関数の戻り値を明示的に無視していることを伝えます。

#include <pthread.h>

void initialize() {
    pthread_attr_t attr;

    (void)pthread_attr_init(&attr); 
}
修正 — 戻り値をテスト

考えられる 1 つの修正方法として、pthread_attr_init の戻り値をテストして、エラーをチェックします。

#include <pthread.h>
#include <stdlib.h>
#define fatal_error() abort()

void initialize() {
    pthread_attr_t attr;
    int result;

    result = pthread_attr_init(&attr);
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }
}
例 — 重要な関数の戻り値が無視される
#include <pthread.h>
extern void *start_routine(void *);

void returnnotchecked() {
    pthread_t thread_id;
    pthread_attr_t attr;
    void *res;

    (void)pthread_attr_init(&attr);
    (void)pthread_create(&thread_id, &attr, &start_routine, ((void *)0)); //Noncompliant
    pthread_join(thread_id,  &res);  //Noncompliant
}

この例では、2 つの重要な関数 pthread_create および pthread_join を呼び出しています。pthread_create の戻り値は void にキャストすることで無視されますが、pthread_create は (要注意の関数であるだけでなく) 重要な関数であるため、Polyspace ではこの "要注意の関数の戻り値がチェックされていません" という欠陥が無視されません。他の重要な関数 pthread_join は暗黙的に無視される値を返します。pthread_join は、pthread_create の戻り値を使用しますが、この戻り値はチェックされていません。

修正 - 重要な関数の戻り値をテスト

この欠陥の修正として、これらの重要な関数の戻り値をチェックして、関数が想定どおりに実行されることを検証します。

#include <pthread.h>
#include <stdlib.h>
#define fatal_error() abort()

extern void *start_routine(void *);

void returnnotchecked() {
    pthread_t thread_id;
    pthread_attr_t attr;
    void *res;
    int result;

    (void)pthread_attr_init(&attr);
    result = pthread_create(&thread_id, &attr, &start_routine, NULL);
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }

    result = pthread_join(thread_id,  &res);
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }
}
例 — ユーザー指定の関数から返された値を無視する

この例では、関数 foo() は重要で要注意の関数であり、関数 bar() は要注意の関数です。


extern int foo(void);            /* Critical */
extern int bar(void);            /* Senstive */
extern void do_something(void);  /* Non-critical/sensitive */

void example() {

	(void)foo();//Noncompliant
	int ret = bar(); //Noncompliant
	do_something(); // Compliant
}

  • foo() は重要で要注意の関数であるため、その戻り値を void にキャストして明示的に無視することは、このルールに対する違反です。

  • bar() の戻り値は変数 ret に格納されますが、コード内で使用されたりチェックされたりすることはありません。この無視される戻り値は、このルールに対する違反です。

これら 2 つの関数をそれぞれ重要で要注意の関数、要注意の関数として指定するには、Datalog ファイルを作成して次の内容を追加します。

.include "models/interfaces/sensitive_function.dl"


sensitive_function.is_critical("foo").
sensitive_function.is_sensitive("bar").
解析を実行するときに、この Datalog ファイルをオプション -code-behavior-specifications の入力として指定します。

チェック情報

グループ: Rec.03.式 (EXP)

バージョン履歴

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.