The signal()
function has implementation-defined behavior and behaves differently, for example, on Windows than it does on many Unix systems.
The following code example illustrates this behavior:
Code Block |
---|
#include <stdio.h> #include <signal.h> volatile sig_atomic_t e_flag = 0; void handler(int signum) { e_flag = 1; } int main(void) { signal(SIGINT, handler); while (!e_flag) {} puts("Escaped from first while ()"); e_flag = 0; while (!e_flag) {} puts("Escaped from second while ()"); return 0; } |
Many Unix (and Unix-like) systems automatically reinstall signal handlers upon handler execution, meaning that the signal handler defined by the user is left in place until it is explicitly removed. For example, when this code is compiled with gcc 3.4.4 and executed under Red Hat Linux, the SIGINT
is captured both times by handler
.
...
When a signal handler is installed with the signal()
function in Windows and some Unix systems, the default action is restored for that signal after the signal is triggered. This means that signal handlers are not automatically reinstalled. For example, when this code is compiled with Microsoft Visual Studio 2005 version 8.0, only the first SIGINT
is captured by handler
.
...
Asynchronous signals may originate from potentially hostile sources outside the process. Consequently, vulnerabilities may exist in cases where the signal handler persistence behavior is inconsistent with the developer's expectations, for example, the developer expects the signal handler to persist when it does not.
Non-Compliant Code Example
...
This non-compliant code example fails to persist make the signal handler persist on Windows platforms and on those Unix systems where handlers are not persistent by default.
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handling code */ } |
Non-Compliant Code Example
...
A common solution to attempt to create persistent signal handlers is to call signal()
from within the handler itself, consequently unresetting the reset signal.
Code Block | ||
---|---|---|
| ||
void handler(int signum) { #ifdef WINDOWS signal(signum, handler); #endif /* handling code */ } |
Unfortunately this solution still contains a race window, starting when the host environment resets the signal and ending when the handler calls signal()
. During that time, a second signal sent to the program will trigger the default signal behavior, thereby defeating the persistent behavior.
...
The POSIX sigaction()
function assigns handlers to signals in a similar manner to the C99 signal()
function, but also allows signal masks handler persistence to be set explicitly. Consequently, sigaction()
can be used to eliminate the race window on non-persistent environments.controlled via the SA_RESETHAND flag. (Leaving the flag clear makes the handler persistent.)
Code Block | ||
---|---|---|
| ||
/* Equivalent to signal(SIGUSR1, handler) but make signal persistent */ struct sigaction act; act.sa_handler = &handler; act.sa_flags = 0; if (sigfillsetsigemptyset( &act.sa_mask) != 0) { /* handle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handle error */ } |
...
This non-compliant code example fails to reset the signal handler to its default behavior on those Unix systems where handlers are persistent by default.
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handling code */ } |
...
A C99-compliant solution to reset the handler on a Unix system is to rebind the signal to the implementation-defined default handler in the first line of the handler itself.
...
The POSIX sigaction()
function assigns handlers to signals in a similar manner to the C99 signal()
function, but also allows signal masks handler persistence to be set explicitly. Consequently, sigaction()
can be used to eliminate the race window on controlled via the SA_RESETHAND flag. (Setting the flag makes the handler non-persistent environments.)
Code Block | ||
---|---|---|
| ||
/* Equivalent to signal( SIGUSR1, handler); */ but make signal non-persistent */ struct sigaction act; act.sa_handler = &handler; act.sa_flags = SA_RESETHAND; if (sigemptyset( &act.sa_mask) != 0) { /* handle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handle error */ } |
...