JPCERT コーディネーションセンター

SIG35-C. シグナルハンドラ SIGSEGV、SIGILL、SIGFPE から復帰しない

C 標準のセクション 7.14.1.1 (シグナル) [ISO/IEC 9899:2011] には、次のように、シグナルハンドラ SIGSEGVSIGILL、または SIGFPE から戻ったときのプログラムの動作は未定義であると記載されている。

sig の値が SIGFPESIGILLSIGSEGV 又は計算例外に対応する他の処理系定義の値である場合、その動作は、未定義とし、その他の場合、プログラムは、割り込まれたところから実行を再開する。

さらに、膨大な量の命令を発生させる浮動小数点命令の後は、SIGFPE が捕捉されない場合がある。

違反コード

次のコード例では、ユーザからの入力が 0 の場合、除算により SIGFPE シグナルがプログラムに送信される。

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

volatile sig_atomic_t denom;

void sighandle(int s){
  /* 問題となる volatile を修正 */
  if (denom == 0) {
    denom = 1;
  }
  /* すべて問題ない */
  return;
}

int main(int argc, char *argv[]){
  int result = 0;
    
  if (argc < 2) {
    return 0;
  }
  denom = (int)strtol(argv[1], (char **)NULL, 10);
    
  signal(SIGFPE,(*sighandle));

  result = 100/denom;
  return 0;
}

この違反コードが GCC 4.3 または GCC 3.4 でコンパイルされると、入力が 0 の場合に無限ループを引き起こす。これは、SIGFPE ハンドラが他のすべてのシグナル処理のルールに従っており、エラー条件を修正しようとしたとしても、プログラムは意図したとおりに動作しないことを示している。

適合コード

以下の適合コードにおいて、シグナルハンドラ SIGFPESIGILL または SIGSEGV から抜けるための可搬性のある安全な唯一の方法は、abort() または /_Exit() を使用することである。SIGFPE の場合、デフォルトのハンドラが abort() を呼び出すため、ユーザ定義のハンドラは実質的に不要となる。以下のコード例では、ハンドラは整合性のためだけに使用されている。

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

volatile sig_atomic_t denom;

void sighandle(int s){
  /* 回復しない */
  abort();
}

int main(int argc, char *argv[]){
  int result = 0;
    
  if (argc < 2) {
    return 0;
  }
  denom = (int)strtol(argv[1], (char **)NULL, 10);
    
  signal(SIGFPE,(*sighandle));

  result = 100/denom;
  return 0;
}
処理系固有の詳細

いくつかの実装は、1 つまたは複数のシグナルハンドラから戻るプログラムのために有益な動作を定義している。たとえば Solaris は、プログラムが安全に戻ることのできる SIGFPE ハンドラを設定するために特別に sigfpe() 関数を提供している。また、Sun もシグナル SIGTRAPSIGBUS および SIGEMT に対するプラットフォーム固有の計算上の例外を提供している。GNU libsigsegv は、IGSEGV ハンドラから戻る機能を利用して、ユーザモードでのページレベルのメモリ管理を実装している。

リスク評価

シグナル SIGSEGVSIGILL または SIGFPE を処理しようとするコードはまれである。しかし、これらのシグナルの処理を必要としているコードは、通常は問題を修正するために設計変更が必要になる。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

SIG35-C

P1

L3

参考資料
Giri Mandalika Mandalika's Scratchpad
翻訳元

これは以下のページを翻訳したものです。

SIG35-C. Do not return from SIGSEGV, SIGILL, or SIGFPE signal handlers (revision 21)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter