メインコンテンツ

信号ハンドラー内からの信号呼び出し

非永続的な信号ハンドラーが Windows システム内で signal() を呼び出すことによる競合状態の発生

説明

この欠陥は、Windows® プラットフォームで信号ハンドラーから関数 signal() を呼び出す場合に発生します。

この欠陥は、Visual Studio コンパイラを指定した場合にのみ検出されます。コンパイラ (-compiler) を参照してください。

リスク

関数 signal() は、信号と信号ハンドラー関数を関連付けます。信号の受信後にこの関連付けが削除される Windows などのプラットフォーム上では、"信号ハンドラー内で" 再度関数 signal() を呼び出して、関連付けを再設定することができます。

ただし、信号ハンドラーを永続的にするこの試みによって、競合状態が引き起こされやすくなります。Windows プラットフォームでは、信号ハンドラーが実行を開始してから、関数 signal が再度呼び出されるまでの間に、アクティブになっているのは既定の信号処理 SIG_DFL です。この間に 2 つ目の信号が受信されると、ユーザーの期待に反して、カスタム信号ハンドラーではなく既定の信号ハンドラーが示されます。

修正方法

Windows プラットフォームでは信号ハンドラーから signal() を呼び出さないようにします。

すべて展開する

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



volatile sig_atomic_t e_flag = 0;

void sig_handler(int signum)
{
    int s0 = signum;
    e_flag = 1;
	
	/* Call signal() to reestablish sig_handler 
	upon receiving SIG_ERR. */
   
    if (signal(s0, sig_handler) == SIG_ERR) 
    {
        /* Handle error */       
    }
}

void func(void)
{
        if (signal(SIGINT, sig_handler) == SIG_ERR)
        {
            /* Handle error */
            
        }
  /* more code */
}        
      

この例では、sig_handler() の定義にハンドラーが SIG_ERR をキャッチした際の signal() の呼び出しが含まれています。Windows プラットフォームでは、信号ハンドラーは永続的ではありません。このコードは、競合状態を発生させる可能性があります。

この問題は、visual15.x などのコンパイラを解析用として指定した場合にのみ検出されます。

修正 — 信号ハンドラーから signal() を呼び出さない

Windows 上で信号ハンドラーを永続的にする試みを避けます。コードで Windows プラットフォームで永続的な信号ハンドラーを使用する必要がある場合、徹底的なリスク解析をしてから永続的な信号ハンドラーを使用します。

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



volatile sig_atomic_t e_flag = 0;


void sig_handler(int signum)
{
    int s0 = signum;
    e_flag = 1;
    /* No call to signal() */
}

int main(void)
{
    
        if (signal(SIGINT, sig_handler) == SIG_ERR)
        {
            /* Handle error */
            
        }
}
 

結果情報

グループ: プログラミング
言語: C | C++
既定値: 手書きコードはオン、生成コードはオフ
コマンド ライン構文: SIG_HANDLER_CALLING_SIGNAL
影響度: Medium

バージョン履歴

R2017b で導入