Versions Compared

Key

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

...

A signal handler is allowed to call signal(), and if that fails, signal() returns SIG_ERR and sets errno to a positive value. However, if the event that caused a signal was external (not the result of the program calling abort() or raise()), the only functions the signal handler may call are _Exit() or abort(), or it may call signal() on the signal currently being handled, and if signal() fails, the value of errno is indeterminate.

This rule is an instance of ERR33-C. Detect and handle standard library errors.This rule is also a special case of SIG31-C. Do not access 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 in subclause , 7.14.1.1, paragraph 5, makes a special exception for errno in this case, saying the only thing that is allowed to go wrong is that errno can allowing errno to take on an indeterminate value but specifying that there is no other undefined behavior. This special exception makes it possible to call signal() from within a signal handler without risking undefined behavior, but the handler, and any code executed after the handler returns, must not depend on the value of errno being meaningful.

Noncompliant Code Example

The handler() function in this noncompliant code example attempts to restore default handling for the signal indicated by signum. If the request to set the signal to default can be honored, the signal() function returns the value of the signal handler for the most recent successful call to the signal() function for the specified signal. Otherwise, a value of SIG_ERR is returned and a positive value is stored in errno. Unfortunately, the value of errno is indeterminate because the handler() function is called when an external signal is raised, so any attempt to read errno (for example, by the perror() function) is undefined behavior:

Code Block
bgColor#FFcccc
langc
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

typedef void (*pfv)(int);

void handler(int signum) {
  pfv old_handler = signal(signum, SIG_DFL);
  if (old_handler == SIG_ERR) {
    perror("SIGINT handler"); /* Undefined behavior */
    /* Handle error condition */
  }
}

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

  /* Main code loop */

  return EXIT_SUCCESS;
}

The call to perror() from handler() also violates SIG30-C. Call only asynchronous-safe functions within signal handlers.

Compliant Solution

The This compliant solution does not reference errno and does not return from the signal handler if the signal() call fails:

Code Block
bgColor#ccccff
langc
#include <signal.h>
#include <stdlib.h>
#include <stdio.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 EXIT_SUCCESS;
}

...

POSIX is less restrictive than C about what applications can do in signal handlers. It has a long list of asynchronous-safe functions that can be called . (See see SIG30-C. Call only asynchronous-safe functions within signal handlers). ) Many of these functions set errno on error, which can lead to a signal handler being executed between a call to a failed function and the subsequent inspection of errno. Consequently, 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 containing code that might alter errno always save the value of errno on entry and restore it before returning.

The signal handler in this noncompliant code example alters the value of errno, and as a result, it can cause incorrect error handling if executed between a failed function call and the subsequent inspection of errno.:

Code Block
bgColor#FFcccc
langc
#include <signal.h>
#include <stdlib.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 EXIT_SUCCESS;
}

Compliant Solution (POSIX)

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

...

Referencing indeterminate values of errno is undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR32-C

Low

Unlikely

Low

P3

L3

...

Bibliography

[ISO/IEC 9899:2011]Subclause 77.14.1.1, "The signal Function"

...