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

Compare with Current View Page History

« Previous Version 68 Next »

A signal is a mechanism for transferring control that is typically used to notify a process that an event has occurred. That process can then respond to that event accordingly. C99 provides functions for sending and handling signals within a C program.

Processes handle signals by registering a signal handler using the signal() function, which is specified as

void (*signal(int sig, void (*func)(int)))(int);

This is conceptually equivalent to

typedef void (*sighandler_t)(int signum);
extern sighandler_t signal(
  int signum, 
  sighandler_t handler
);

Signal handlers can be interrupted by signals, including their own. If a signal is not reset before its handler is called, the handler can interrupt its own execution. A handler that always successfully executes its code despite interrupting itself or being interrupted is async-signal-safe.

Some platforms provide the ability to mask signals while a signal handler is being processed. If a signal is masked while its own handler is processed, the handler is noninterruptible and need not be async-signal-safe. However, even when a signal is masked while its own handler is processed, the handler must still avoid invoking async-signal-safe unsafe functions because their execution may be (or have been) interrupted by another signal.

Vulnerabilities can arise if a signal handler that is not async-signal-safe is interrupted with any unmasked signal, including its own.

Noncompliant Code Example

This noncompliant code example registers a single signal handler to process both SIGUSR1 and SIGUSR2. The variable sig2 should be set to 1 if one or more SIGUSR1 signals are followed by SIGUSR2, essentially implementing a finite state machine within the signal handler.

#include <signal.h>

volatile sig_atomic_t sig1 = 0;
volatile sig_atomic_t sig2 = 0;

void handler(int signum) {
  if (signum == SIGUSR1) {
    sig1 = 1;
  }
  else if (sig1) {
    sig2 = 1;
  }
}

int main(void) {
  if (signal(SIGUSR1, handler) == SIG_ERR) {
    /* Handle error */
  }
  if (signal(SIGUSR2, handler) == SIG_ERR) {
    /* Handler error */
  }

  while (sig2 == 0) {
    /* Do nothing or give up CPU for a while */
  }

  /* ... */

  return 0;
}

Unfortunately, there is a race condition in the implementation of handler(). If handler() is called to handle SIGUSR1 and is interrupted to handle SIGUSR2, it is possible that sig2 will not be set.

Compliant Solution (POSIX)

The POSIX sigaction() function assigns handlers to signals in a similar manner to the C99 signal() function, but it also allows signal masks to be set explicitly. Consequently, sigaction() can be used to prevent a signal handler from interrupting itself.

#include <signal.h>
#include <stdio.h>

volatile sig_atomic_t sig1 = 0;
volatile sig_atomic_t sig2 = 0;

void handler(int signum) {
  if (signum == SIGUSR1) {
    sig1 = 1;
  }
  else if (sig1) {
    sig2 = 1;
  }
}

int main(void) {
  struct sigaction act;
  act.sa_handler = &handler;
  act.sa_flags = 0;
  if (sigemptyset(&act.sa_mask) != 0) {
    /* Handle error */
  }
  if (sigaddset(&act.sa_mask, SIGUSR1)) {
    /* Handle error */
  }
  if (sigaddset(&act.sa_mask, SIGUSR2)) {
    /* Handle error */
  }

  if (sigaction(SIGUSR1, &act, NULL) != 0) {
    /* Handle error */
  }
  if (sigaction(SIGUSR2, &act, NULL) != 0) {
    /* Handle error */
  }

  while (sig2 == 0) {
    /* Do nothing or give up CPU for a while */
  }

  /* ... */

  return 0;
}

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

Risk Assessment

Interrupting a noninterruptible signal handler can result in a variety of vulnerabilities [Zalewski 2001].

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

SIG00-C

high

likely

high

P9

L2

Related Vulnerabilities

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

Related Guidelines

CERT C++ Secure Coding Standard: SIG00-CPP. Mask signals handled by noninterruptible signal handlers

CERT C++ Secure Coding Standard: CWE-662, "Insufficient Synchronization"

ISO/IEC 2003 Section 5.2.3, "Signals and interrupts"

Bibliography

[Dowd 2006] Chapter 13, "Synchronization and State" (Signal Interruption and Repetition)
[Open Group 2004] longjmp
[OpenBSD] signal() Man Page
[Zalewski 2001]


11. Signals (SIG)      11. Signals (SIG)      

  • No labels