メインコンテンツ

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

CERT C: Rule ERR32-C

errno の不確定の値に依存しない

説明

ルール定義

errno の不確定の値に依存しないようにします。1

Polyspace 実装

ルール チェッカーは、"信号ハンドラーでの errno の不適切な使用" をチェックします。

すべて展開する

問題

[信号ハンドラーでの errno の不適切な使用] は、信号ハンドラーで以下のいずれかの関数を呼び出す場合に発生します。

  • signal: 信号ハンドラーで関数 signal を呼び出し、次に errno の値を読み取ります。

    たとえば、信号ハンドラー関数 handlersignal を呼び出し、次に errno を読み取る perror を呼び出します。

    typedef void (*pfv)(int);
    
    void handler(int signum) {
      pfv old_handler = signal(signum, SIG_DFL);
      if (old_handler == SIG_ERR) {
        perror("SIGINT handler"); 
      }
    }

  • errno を設定する POSIX® 関数: 信号ハンドラーで errno を設定する POSIX 関数を呼び出しても、その信号ハンドラーから返されたときに errno は復元されません。

    たとえば、信号ハンドラー関数 handler で、errno を変更する waitpid を呼び出しても、返される前に errno は復元されません。

    #include <stddef.h>
    #include <errno.h>
    #include <sys/wait.h>
    
    void handler(int signum) {
      int rc = waitpid(-1, NULL, WNOHANG);
      if (ECHILD != errno) {
      }
    }

リスク

チェッカーによってフラグが設定される各ケースでは、errno の不確定の値に依存するリスクがあります。

  • signal:信号ハンドラーでの signal の呼び出しに失敗した場合、errno の値が不確定になります (C11 規格、節 7.14.1.1).errno の特定の値に依存すると、予期しない結果になる可能性があります。

  • errno を設定する POSIX 関数: errno を設定する関数は失敗時に errno を設定します。信号ハンドラーが呼び出されて、その信号ハンドラー自体により errno を設定する関数が呼び出された後に errno を読み取る場合、予期しない結果になる可能性があります。

修正方法

errno の不確定の値に依存するリスクがある状況を回避します。

  • signal: 信号ハンドラーで関数 signal を呼び出した後に、errno を読み取ったり、errno を読み取る関数を使用したりしないようにします。

  • errno を設定する POSIX 関数: 信号ハンドラーで errno を設定する関数を呼び出す前に、errno を一時変数に保存します。信号ハンドラーから返される前にこの変数から errno を復元します。

例 - 信号ハンドラーでの signal 呼び出し後の errno の読み取り
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        perror("SIGINT handler");
    }
} //Noncompliant

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 0;
}

この例では、SIGINT 信号を処理するために関数 handler が呼び出されます。handler の本体では、関数 signal が呼び出されます。この呼び出し後には errno の値が不確定になります。perrorerrno の値に依存するため、関数 perror が呼び出されるとチェッカーによって欠陥が報告されます。

修正 — signal 呼び出し後の errno の読み取りを回避

1 つの修正方法として、信号ハンドラーで関数 signal を呼び出した後に errno を読み取らないようにします。修正した以下のコードでは、関数 perror ではなく、fatal_error マクロを介して関数 abort を呼び出します。

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

#define fatal_error() abort()

void handler(int signum) {
    if (signal(signum, SIG_DFL) == SIG_ERR) {
        fatal_error();
    }
} 

int func(void) {
    if (signal(SIGINT, handler) == SIG_ERR) {
        /* Handle error */
        fatal_error();
    }
    /* Program code */
    if (raise(SIGINT) != 0) {
        /* Handle error */
        fatal_error();
    }
    return 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.