...
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) { if (signal(SIGINT, handler) == SIG_ERR) { /* handleHandle error */ } 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, SIGINT
is captured both times by handler
.
Code Block |
---|
% ./SIG01-Ctest ^C Escaped from first while () ^C Escaped from second while () % |
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
.
Code Block |
---|
> SIG01-Ctest.exe ^C Escaped from first while () ^C > |
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handleHandle signal */ } |
Noncompliant Code Example
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { if (signal(signum, handler) == SIG_ERR) { /* handleHandle error */ } /* handleHandle signal */ } |
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, defeating the persistent behavior (see SIG34-C. Do not call signal() from within interruptible signal handlers).
...
Code Block | ||
---|---|---|
| ||
/* Equivalent to signal(SIGUSR1, handler) but makes * signal persistent */ struct sigaction act; act.sa_handler = handler; act.sa_flags = 0; if (sigemptyset(&act.sa_mask) != 0) { /* handleHandle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } |
POSIX recommends sigaction()
and deprecates signal()
. Unfortunately, sigaction()
is not defined in C99 and is consequently not as portable a solution.
...
Nonpersistent Handlers
Errors may also occur when the developer expects the default action to be restored for a signal but instead the signal handler persists.
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handleHandle signal */ } |
Compliant Solution (UNIX and Windows)
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { #ifndef WINDOWS if (signal(signum, SIG_DFL) == SIG_ERR) { /* handlerHandler error */ } #endif /* handleHandle signal */ } |
With the compliant solution for UNIX, there is no race condition that can be exploited by an attacker in sending a second signal. And that is because a second signal sent to the handler, before the latter calls signal(signum, SIG_DFL)
, will only cause the handler to restart and call signal()
anyway.
...
The POSIX sigaction()
function assigns handlers to signals in a manner similar to the C99 signal()
function but also allows signal handler persistence to be controlled via the SA_RESETHAND
flag. (Setting the flag makes the handler non-persistentnonpersistent.)
Code Block | ||
---|---|---|
| ||
/* Equivalent to signal(SIGUSR1, handler) but makes * signal non-persistentnonpersistent */ struct sigaction act; act.sa_handler = handler; act.sa_flags = SA_RESETHAND; if (sigemptyset(&act.sa_mask) != 0) { /* handleHandle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handleHandle error */ } |
Risk Assessment
...