You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 16 Next »

A signal handler should not reassert its desire to handle its own signal. This is often done on non-persistent platforms, that is, when a signal handler receives a signal that is bound to a handler, these platforms unbind the signal to default behavior before calling the handler.

A signal handler may only call signal() if it does not need to be asynchronous-safe (in other words, all relevant signals are masked, and therefore it may not be interrupted.)

Non-Compliant Code Example

In this non-compliant code example, the signal handler handler() is bound to signum.

void handler(int signum) {
  signal(signum, handler);
  /* handle signal */
}
/* ... */
signal(signum, handler);

Unfortunately 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, thereby 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 signal() function is redundant.

Compliant Solution

For persistent platforms, calling the signal() function from within the signal handler is unnecessary.

void handler(int signum) {
  /* handle signal */
}
/* ... */
signal(signum, handler);

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 OSs.

void handler(int signum) {
  /* handle signal */
}
/* ... */
/* Equivalent to signal( signum, handler);
   but make signal persistent */
struct sigaction act;
act.sa_handler = handler;
act.sa_flags = 0;
if (sigemptyset( &act.sa_mask) != 0) {
  /* handle error */
}
if (sigaction(signum, &act, NULL) != 0) {
  /* handle error */
}

While the handler in this example does not call signal(), it safely can because the signal is masked and so 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 only masks the signal being delivered.

POSIX recommends sigaction() and deprecates signal(). Unfortunately, sigaction() is not C99-compliant and is not supported on some platforms, including Windows.

Exceptions

SIG34-EX1: On a machine with persistent signal handlers, it is safe for a handler to modify the behavior for its own signal. This would include having the signal be ignored, reset to default behavior, or handled by a different handler. A handler assigning itself to its own signal is also safe, as it is a no-op. The handler is impervious to a race condition because multiple invocations of its signal will merely cause it to "interrupt itself" until it manages to reassign its signal.

The following code example resets a signal to the system's default behavior.

void handler(int signum) {
#ifndef WINDOWS
  signal(signum, SIG_DFL);
#endif
  /* handle signal */
}
/* ... */
signal(signum, handler);

Not all systems have persistent signal handlers. For more info, see SIG01-A. Understand implementation-specific details regarding signal handler persistence.

Risk Assessment

Two signals in quick succession can trigger the race condition on non-persistent platforms, causing the signal's default behavior despite a handler's attempt to override it.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

SIG34-C

low

unlikely

low

P3

L3

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

[[ISO/IEC 9899-1999TR2]] Section 7.14.1.1, "The signal function"


SIG33-C. Do not recursively invoke the raise() function      11. Signals (SIG)       12. Error Handling (ERR)

  • No labels