メインコンテンツ

非同期安全ではない信号ハンドラーから呼び出された関数 (厳密な ISO C)

割り込み関数の呼び出しは未定義のプログラム動作を引き起こす

説明

この欠陥は、C 標準によると非同期安全ではない関数を信号ハンドラーが呼び出した場合に発生します。非同期安全な関数は、実行中のどの時点でも割り込んで再度呼び出すことができ、不整合な状態を発生させません。また、不整合な状態の可能性があるグローバル データを正しく処理できます。

C 標準では、POSIX 規格に従って非同期安全である関数のセットと比較して、より厳密な関数のサブセットを非同期安全として定義しています。"非同期安全ではない信号ハンドラーから呼び出された関数 (厳密な ISO C)" は、関数が POSIX 規格に従って非同期安全である場合でも、信号ハンドラーがそのサブセットに含まれていない関数を呼び出したときに欠陥を報告します。

POSIX 規格に従って非同期安全ではない関数の呼び出しをチェックするには、チェッカーの "非同期安全ではない信号ハンドラーから呼び出された関数" を有効にします。

信号ハンドラーで、非同期安全ではない関数を呼び出す別の関数を呼び出す場合、その信号ハンドラーの関数呼び出しに欠陥が表示されます。欠陥のトレースバックには、信号ハンドラーから非同期安全ではない関数までの絶対パスが表示されます。

リスク

信号ハンドラーが呼び出されたときに、プログラムの実行が割り込まれます。ハンドラーが終了した後、割り込まれた箇所からプログラムの実行が再開します。関数が非同期安全ではない限り、割り込み時に関数が実行中の場合、信号ハンドラーからのその関数の呼び出しは未定義の動作です。

修正方法

C 標準では、次の関数を非同期安全として定義しています。信号ハンドラーから次の関数を呼び出すことができます。

  • abort()

  • _Exit()

  • quick_exit()

  • signal()

すべて展開する

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <syslog.h>
#include <unistd.h>

void SIG_ERR_handler(int signum)
{
    int s0 = signum;
    /* SIGTERM specific handling */
}

void sig_handler(int signum)
{
    int s0 = signum;
	/* Call raise() */ 
    if (raise(SIGTERM) != 0) { 
        /* Handle error */
    }
}

int finc(void)
{
    if (signal(SIGTERM, SIG_ERR_handler) == SIG_ERR)
    {
        /* Handle error */
    }
    if (signal(SIGINT, sig_handler) == SIG_ERR)
    {
        /* Handle error */
    }
    /* Program code */
    if (raise(SIGINT) != 0)
    {
        /* Handle error */
    }
    /* More code */
    return 0;
}
        
      

この例では、信号をキャッチするときに sig_handlerraise() を呼び出しています。raise() の実行中にハンドラーが別の信号をキャッチする場合、プログラムの動作は未定義です。

修正 — 信号ハンドラーの raise() の呼び出しを削除

C 標準によると、信号ハンドラーから安全に呼び出すことができる関数は、abort()_Exit()quick_exit()、および signal() のみです。

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <syslog.h>
#include <unistd.h>

void SIG_ERR_handler(int signum)
{
    int s0 = signum;
    /* SIGTERM specific handling */
}
void sig_handler(int signum)
{
    int s0 = signum;
	
  
}

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

結果情報

グループ: プログラミング
言語: C | C++
既定値: オフ
コマンド ライン構文: SIG_HANDLER_ASYNC_UNSAFE_STRICT
影響度: Medium

バージョン履歴

R2017b で導入