A signal handler should not reassert its desire to handle its own signal. This is often done on non-persistent nonpersistent platforms , that is, platforms that, upon receiving a signal, unbind the signal to default behavior before calling the bound signal handler. See SIG01-C. Understand implementation-specific details regarding signal handler persistence.
A signal handler may only call signal()
only if it does not need to be asynchronous-safe (in other words, all relevant signals are masked, and consequently it may not be interrupted.)
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { if (signal(signum, handler) == SIG_ERR) { /* handleHandle error */ } /* handleHandle signal */ } /* ... */ if (signal(SIGUSR1, handler) == SIG_ERR) { /* handleHandle error */ } |
On non-persistent nonpersistent platforms, this solution 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, consequently defeating the persistent behavior implied by the call to signal()
from within the handler to reassert the binding.
If the environment is persistent (that is, it does not reset the handler when the signal is received), the handler's call signal()
from within the handler(0
function is redundant.
Compliant Solution
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handleHandle signal */ } /* ... */ if (signal(SIGUSR1, handler) == SIG_ERR) { /* handleHandle error */ } |
Compliant Solution (POSIX)
POSIX defines the sigaction()
function, which assigns handlers to signals like signal()
but also allows the caller to explicitly set persistence. Consequently, the sigaction()
function can be used to eliminate the race window on non-persistent OSsnonpersistent operating systems.
Code Block | ||
---|---|---|
| ||
void handler(int signum) { /* handleHandle signal */ } /* ... */ 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 */ } |
While the handler in this example does not call signal()
, it could safely do so safely because the signal is masked, and consequently the handler cannot be interrupted. If the same handler is installed for more than one signal number, it would be necessary to mask the signals explicitly in act.sa_mask
to ensure that the handler cannot be interrupted, because the system masks only masks the signal being delivered.
...
Code Block | ||
---|---|---|
| ||
void handler(int signum) { #ifndef WINDOWS if (signal(signum, SIG_DFL) == SIG_ERR) { /* handleHandle error */ } #endif /* handleHandle signal */ } /* ... */ if (signal(SIGUSR1, handler) == SIG_ERR) { /* handleHandle error */ } |
Risk Assessment
Two signals in quick succession can trigger the race condition on non-persistent nonpersistent platforms, causing the signal's default behavior despite a handler's attempt to override it.
...