メインコンテンツ

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

CERT C: Rule ERR30-C

Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure

説明

ルール定義

errno を設定することがわかっているライブラリ関数を呼び出す前に errno をゼロに設定し、その関数が失敗を示す値を返した後でのみ errno をチェックします。1

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • errno の不適切な使用

  • errno がリセットされていません

すべて展開する

問題

errno の不適切な使用は、errno をチェックしてもエラーがないことが保証されない状況で、errno でエラー状態をチェックしている場合に発生します。場合によっては、errno をチェックすることによって誤検知につながる可能性があります。

たとえば、次の関数呼び出し後に errno をチェックします。

  • fopen: ISO® 規格に従う場合、この関数はエラーで errno を設定しない可能性があります。

  • atof: ISO 規格に従う場合、この関数は errno を設定しません。

  • signal: この関数が SIG_ERR エラー インジケーターを返す場合のみ、errno の値がエラーを示します。

リスク

ISO C 標準では、これらの関数は必ずしもエラーで errno を設定しません。関数が errno を設定するかどうかは、実装によって異なります。

エラーを検出するため errno のみをチェックする場合、このチェックの妥当性も実装によって異なります。

場合によっては、関数が特定のエラー インジケーターを返す場合のみ、errno の値がエラーを示します。関数の戻り値をチェックする前に errno をチェックする場合、誤検出が発生する可能性があります。

修正方法

エラーの検出方法の詳細は、その特定の関数のドキュメンテーションを参照してください。

通常、そのような関数はエラーを示すために帯域外エラー インジケーターを返します。次に例を示します。

  • fopen は、エラーが発生すると NULL ポインターを返します。

  • signal は、SIG_ERR エラー インジケーターを返し、errno に正の値を設定します。この関数の戻り値をチェックした後のみ errno をチェックします。

例 - fopen 呼び出し後の errno の不適切なチェック
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define fatal_error() abort()

const char *temp_filename = "/tmp/demo.txt";

FILE *func()
{
    FILE *fileptr;
    errno = 0;
    fileptr = fopen(temp_filename, "w+b");
    if (errno != 0) { //Noncompliant
        if (fileptr != NULL) {
            (void)fclose(fileptr);
        }
        /* Handle error */
        fatal_error();
    }
    return fileptr;
}

この例では、errnofopen の呼び出し後にチェックされる最初の変数です。エラーが発生すると fopenerrno を非ゼロの値に変更すると想定しています。エラーで errno を設定しない fopen の実装を使ってこのコードを実行すると、エラー状態を見落とすことになります。この場合、fopen は検出をエスケープする NULL ポインターを返す可能性があります。

修正 — fopen 呼び出し後に戻り値をチェック

1 つの修正方法として、fopen の戻り値が NULL ポインターかどうかのみをチェックします。

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

#define fatal_error() abort()

const char *temp_filename = "/tmp/demo.txt";

FILE *func()
{
    FILE *fileptr;
    fileptr = fopen(temp_filename, "w+b");
    if (fileptr == NULL) { 
        fatal_error();
    }
    return fileptr;
}
問題

errno がリセットされていませんは、エラー状態を示すために errno を設定する関数を呼び出す前に errno をリセットしていない場合に発生します。ただし、関数呼び出し後には、errno でエラー状態をチェックしています。

リスク

errno を設定する関数は、errno を、エラー状態を示す非ゼロの値に設定します。

errno を設定する関数を呼び出す前に errno をゼロに設定していなかった場合、errno を設定する関数を以前に呼び出したときに設定された非ゼロの値が、errno に残っている可能性があります。この場合、errno を使用してエラーを確認すると、最新の呼び出しで発生したエラーであると誤って判断する可能性があります。

errno はプログラム起動時に 0 に設定されますが、エラーの発生後に自動的にリセットされることはありません。必要に応じて、errno を明示的に 0 に設定しなければなりません。

修正方法

エラー状態を示すために errno を設定する関数を呼び出す前に、errno を明示的にゼロにリセットします。

例 - strtod を呼び出す前に errno をリセットしていない
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    f1 = strtod (s1, NULL);   //Noncompliant
    if (0 == errno) {         
      double f2 = strtod (s2, NULL); 
        if (0 == errno) {        
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
				  {
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}

この例では、strtod の最初の呼び出しの前に errno を 0 に設定していません。この errno がゼロかどうかをチェックすると、誤検知につながる可能性があります。

修正 — 呼び出し前に errno をリセット

1 つの修正方法として、strtod を呼び出す前に errno を 0 に設定します。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    errno = 0;                   
    f1 = strtod (s1, NULL);
    if (0 == errno) {            
      double f2 = strtod (s2, NULL);  
        if (0 == errno) {       
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
  			{
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}

チェック情報

グループ: Rule 12.エラーの取り扱い (ERR)

バージョン履歴

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.