CWE Rule 364
説明
ルールの説明
The software uses a signal handler that introduces a race condition.
Polyspace 実装
ルール チェッカーは以下の問題をチェックします。
非同期安全ではない信号ハンドラーから呼び出された関数
非同期安全ではない信号ハンドラーから呼び出された関数 (厳密な ISO C)
信号ハンドラー内でのデータ アクセスの共有
例
この問題は、POSIX 標準によると非同期安全ではない関数を、信号ハンドラーが呼び出した場合に発生します。非同期安全な関数は、実行中のどの時点でも割り込んで再度呼び出すことができ、不整合な状態を発生させません。また、不整合な状態の可能性があるグローバル データを正しく処理できます。
信号ハンドラーで、非同期安全ではない関数を呼び出す別の関数を呼び出す場合、その信号ハンドラーの関数呼び出しに欠陥が表示されます。欠陥のトレースバックには、信号ハンドラーから非同期安全ではない関数までの絶対パスが表示されます。
信号ハンドラーが呼び出されたときに、プログラムの実行が割り込まれます。ハンドラーが終了した後、割り込まれた箇所からプログラムの実行が再開します。関数が非同期安全ではない限り、割り込み時に関数が実行中の場合、信号ハンドラーからのその関数の呼び出しは未定義の動作です。
POSIX 規格では、次の関数を非同期安全として定義しています。信号ハンドラーから次の関数を呼び出すことができます。
_exit() | getpgrp() | setsockopt() |
_Exit() | getpid() | setuid() |
abort() | getppid() | shutdown() |
accept() | getsockname() | sigaction() |
access() | getsockopt() | sigaddset() |
aio_error() | getuid() | sigdelset() |
aio_return() | kill() | sigemptyset() |
aio_suspend() | link() | sigfillset() |
alarm() | linkat() | sigismember() |
bind() | listen() | signal() |
cfgetispeed() | lseek() | sigpause() |
cfgetospeed() | lstat() | sigpending() |
cfsetispeed() | mkdir() | sigprocmask() |
cfsetospeed() | mkdirat() | sigqueue() |
chdir() | mkfifo() | sigset() |
chmod() | mkfifoat() | sigsuspend() |
chown() | mknod() | sleep() |
clock_gettime() | mknodat() | sockatmark() |
close() | open() | socket() |
connect() | openat() | socketpair() |
creat() | pathconf() | stat() |
dup() | pause() | symlink() |
dup2() | pipe() | symlinkat() |
execl() | poll() | sysconf() |
execle() | posix_trace_event() | tcdrain() |
execv() | pselect() | tcflow() |
execve() | pthread_kill() | tcflush() |
faccessat() | pthread_self() | tcgetattr() |
fchdir() | pthread_sigmask() | tcgetpgrp() |
fchmod() | quick_exit() | tcsendbreak() |
fchmodat() | raise() | tcsetattr() |
fchown() | read() | tcsetpgrp() |
fchownat() | readlink() | time() |
fcntl() | readlinkat() | timer_getoverrun() |
fdatasync() | recv() | timer_gettime() |
fexecve() | recvfrom() | timer_settime() |
fork() | recvmsg() | times() |
fpathconf() | rename() | umask() |
fstat() | renameat() | uname() |
fstatat() | rmdir() | unlink() |
fsync() | select() | unlinkat() |
ftruncate() | sem_post() | utime() |
futimens() | send() | utimensat() |
getegid() | sendmsg() | utimes() |
geteuid() | sendto() | wait() |
getgid() | setgid() | waitpid() |
getgroups() | setpgid() | write() |
getpeername() | setsid() |
上の表にない関数は非同期安全ではありません。信号ハンドラーから呼び出さないようにします。
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <syslog.h>
#include <unistd.h>
#define SIZE20 20
extern volatile sig_atomic_t e_flag;
void display_info(const char *info)
{
if (info)
{
(void)fputs(info, stderr);
}
}
void sig_handler(int signum)
{
/* Call function printf() that is not
asynchronous-safe */
printf("signal %d received.", signum); //Noncompliant
e_flag = 1;
}
int main(void)
{
e_flag = 0;
if (signal(SIGINT, sig_handler) == SIG_ERR)
{
/* Handle error */
}
char *info = (char *)calloc(SIZE20, sizeof(char));
if (info == NULL)
{
/* Handle Error */
}
while (!e_flag)
{
/* Main loop program code */
display_info(info);
/* More program code */
}
free(info);
info = NULL;
return 0;
}
この例では、信号をキャッチするときに sig_handler
が printf()
を呼び出しています。printf()
の実行中にハンドラーが別の信号をキャッチする場合、プログラムの動作は未定義です。
信号ハンドラーを使用して、フラグの値のみを設定します。e_flag
は、volatile sig_atomic_t
型です。sig_handler
はこれに非同期で安全にアクセスできます。
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <syslog.h>
#include <unistd.h>
#define SIZE20 20
extern volatile sig_atomic_t e_flag;
void display_info(const char *info)
{
if (info)
{
(void)fputs(info, stderr);
}
}
void sig_handler1(int signum)
{
int s0 = signum;
e_flag = 1;
}
int func(void)
{
e_flag = 0;
if (signal(SIGINT, sig_handler1) == SIG_ERR)
{
/* Handle error */
}
char *info = (char *)calloc(SIZE20, 1);
if (info == NULL)
{
/* Handle error */
}
while (!e_flag)
{
/* Main loop program code */
display_info(info);
/* More program code */
}
free(info);
info = NULL;
return 0;
}
この問題は、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) { //Noncompliant
/* 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_handler
が raise()
を呼び出しています。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;
}
この問題は、信号ハンドラー内で共有オブジェクトへのアクセスまたは変更を行った場合に発生します。
共有オブジェクトへのアクセスまたは変更を行う信号ハンドラー関数を定義する場合、ハンドラーは信号を受け取ると共有オブジェクトへのアクセスまたは変更を行います。他の関数が既にその共有オブジェクトにアクセス中の場合、その関数が競合状態を発生させ、データが不整合状態になる可能性があります。
信号ハンドラー内の共有オブジェクトへのアクセスまたは変更を行うには、オブジェクトがロック制御不要のアトミックであることをチェックするか、オブジェクトが整数の場合は volatile sig_atomic_t
として宣言します。
#include <signal.h>
#include <stdlib.h>
#include <string.h>
/* declare global variable. */
int e_flag;
void sig_handler(int signum)
{
/* Signal handler accesses variable that is not
of type volatile sig_atomic_t. */
e_flag = signum; //Noncompliant
}
int func(void)
{
if (signal(SIGINT, sig_handler) == SIG_ERR)
{
/* Handle error */
abort();
}
/* Program code */
if (raise(SIGINT) != 0)
{
/* Handle error */
abort();
}
/* More code */
return 0;
}
この例では、sig_handler
が int
型の変数である e_flag
にアクセスしています。別の関数からの同時アクセスによって、e_flag
が不整合状態になる可能性があります。
volatile sig_atomic_t
型の変数を宣言信号ハンドラーから共有変数にアクセスする前に、変数を int
型ではなく volatile sig_atomic_t
型で宣言します。この型の変数には非同期で安全にアクセスできます。
#include <signal.h>
#include <stdlib.h>
#include <string.h>
/* Declare variable of type volatile sig_atomic_t. */
volatile sig_atomic_t e_flag;
void sig_handler(int signum)
{
/* Use variable of proper type inside signal handler. */
e_flag = signum;
}
int func(void)
{
if (signal(SIGINT, sig_handler) == SIG_ERR)
{
/* Handle error */
abort();
}
/* Program code */
if (raise(SIGINT) != 0)
{
/* Handle error */
abort();
}
/* More code */
return 0;
}
チェック情報
カテゴリ: Signal Errors |
バージョン履歴
R2023a で導入
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Web サイトの選択
Web サイトを選択すると、翻訳されたコンテンツにアクセスし、地域のイベントやサービスを確認できます。現在の位置情報に基づき、次のサイトの選択を推奨します:
また、以下のリストから Web サイトを選択することもできます。
最適なサイトパフォーマンスの取得方法
中国のサイト (中国語または英語) を選択することで、最適なサイトパフォーマンスが得られます。その他の国の MathWorks のサイトは、お客様の地域からのアクセスが最適化されていません。
南北アメリカ
- América Latina (Español)
- Canada (English)
- United States (English)
ヨーロッパ
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)