Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added a paragraph about POSIX, and POSIX examples

...

This rule is a special case of SIG31-C. Do not access or modify shared objects in signal handlers. The object designated by errno is of static storage duration and is not a volatile sig_atomic_t.  As a result, performing any action that would require errno to be set would normally cause undefined behavior.  The C standard makes a special exception for errno in this case, saying the only thing that is allowed to go wrong is that errno takes on an indeterminate value.  This makes it possible to call signal() from within a signal handler without risking completely unrestricted undefined behavior, but the handler, and any code executed after the handler returns, must not depend on the value of errno being meaningful.

POSIX is less restrictive than C99 about what applications can do in signal handlers. It has a long list of async-signal-safe functions that can be called. Many of these functions set errno on error. This can lead to a situation where a signal handler is executed in between a call to a failed function and the subsequent inspection of errno, and thus the value inspected is not the one set by that function but the one set by a function call in the signal handler. POSIX applications can avoid this problem by ensuring that signal handlers which contain code that might alter errno always save the value of errno on entry and restore it before returning.

Non-Compliant Code Example

...

Code Block
bgColor#ccccff
#include <signal.h>
#include <stdlib.h>
#include <string.h>

typedef void (*pfv)(int);

void handler(int signum) {
  pfv old_handler = signal(signum, SIG_DFL);
  if (old_handler == SIG_ERR) {
    abort();
  }
}

int main(void) {
  pfv old_handler = signal(SIGINT, handler);
  if (old_handler == SIG_ERR) {
    perror("SIGINT handler");
    /* handle error condition */
  }

  /* main code loop */

  return 0;
}

Non-Compliant Code Example (POSIX)

The signal handler alters the value of errno, and therefore could cause incorrect error handling if executed in between a failed function call and the subsequent inspection of errno.

Code Block
bgColor#FFcccc

#include <stddef.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

void reaper(int signum) {
  errno = 0;
  for (;;) {
    int rc = waitpid(-1, NULL, WNOHANG);
    if ( (0 == rc) || (-1 == rc && EINTR != errno) )
      break;
  }
  if (ECHILD != errno) {
    /* handle error */
  }
}

int main(void) {
  struct sigaction act;
  act.sa_handler = reaper;
  act.sa_flags = 0;
  if (sigemptyset(&act.sa_mask) != 0) {
    /* handle error */
  }
  if (sigaction(SIGCHLD, &act, NULL) != 0) {
    /* handle error */
  }

  /* ... */

  return 0;
}

Compliant Solution (POSIX)

The compliant solution saves and restores the value of errno in the signal handler.

Code Block
bgColor#ccccff

#include <stddef.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

void reaper(int signum) {
  int save_errno = errno;
  errno = 0;
  for (;;) {
    int rc = waitpid(-1, NULL, WNOHANG);
    if ( (0 == rc) || (-1 == rc && EINTR != errno) )
      break;
  }
  if (ECHILD != errno) {
    /* handle error */
  }
  errno = save_errno;
}

int main(void) {
  struct sigaction act;
  act.sa_handler = reaper;
  act.sa_flags = 0;
  if (sigemptyset(&act.sa_mask) != 0) {
    /* handle error */
  }
  if (sigaction(SIGCHLD, &act, NULL) != 0) {
    /* handle error */
  }

  /* ... */

  return 0;
}

Risk Assessment

...