Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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.

...

Asynchronous signals may originate from potentially hostile sources outside malicious actors external to the process. Consequently, vulnerabilities may exist in cases where the signal handler persistence behavior is inconsistent with the developer's expectations, such as when the developer expects the signal handler to persist but it does not.

...

This non-compliant code example fails to make persist the signal handler persist on Windows platforms and on those UNIX systems where handlers are not persistent by default.

Code Block
bgColor#ffcccc
void handler(int signum) {
  /* Handlehandle Signalsignal */
}

Non-Compliant Code Example

A common solution approach to attempt to create persistent signal handlers is to call signal() from within the handler itself, consequently unresetting the reset signal.

Code Block
bgColor#ffcccc
void handler(int signum) {
  signal(signum, handler);
  /* Handlehandle Signalsignal */
}

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 (see SIG34-C. Do not call signal() from within interruptible signal handlers).

A secure solution would must prevent the environment from resetting the signal in the first place, thereby guaranteeing persistence. Unfortunately, Windows does not provide a secure solution to this problem.

...

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. (Leaving the flag clear makes the handler persistent.)

Code Block
bgColor#ccccff
/* Equivalent to signal(SIGUSR1, handler) but make signal persistent */
struct sigaction act;
act.sa_handler = handler;
act.sa_flags = 0;
if (sigemptyset(&act.sa_mask) != 0) {
  /* Handlehandle Errorerror */
}
if (sigaction(SIGUSR1, &act, NULL) != 0) {
  /* Handlehandle Errorerror */
}

POSIX recommends sigaction() and deprecates signal(). Unfortunately, sigaction() is not defined in C99 and is consequently not as portable a solution.

...

Code Block
bgColor#ffcccc
void handler(int signum) {
  /* Handlehandle Signalsignal */
}

Compliant Solution (UNIX and Windows)

...

Code Block
bgColor#ccccff
void handler(int signum) {
#ifndef WINDOWS
  signal(signum, SIG_DFL);
#endif
  /* Handlehandle Signalsignal */
}

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 merely 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-persistent.)

Code Block
bgColor#ccccff
/* 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) {
  /* Handlehandle Errorerror */
}
if (sigaction(SIGUSR1, &act, NULL) != 0) {
  /* Handlehandle Errorerror */
}

Risk Assessment

Failure to understand implementation-specific details regarding signal handler persistence can lead to unexpected behavior.

...