Versions Compared

Key

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

According to [ISO/IEC 9899-1999:2011], the behavior a program is undefined when

the value of errno is referred to after a signal occurred other than as the result of calling the abort or raise function and the corresponding signal handler obtained a SIG_ERR return from a call to the signal function.

(See undefined behavior 126 of 133 of Annex J.)

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 a special case of rule 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 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  can take on an indeterminate value. This special exception makes it possible to call signal() from within a signal handler without risking unrestricted undefined behavior, but the handler, and any code executed after the handler returns, must not depend on the value of errno being meaningful.

...

Code Block
bgColor#FFcccc
langc

#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) {
    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 0;
}

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

...

Code Block
bgColor#ccccff
langc

#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;
}

...

POSIX is less restrictive than C99 C about what applications can do in signal handlers. It has a long list of asynchronous-safe functions that can be called. (See rule SIG30-C. Call only asynchronous-safe functions within signal handlers.) Many of these functions set errno on error. This can , which can lead to a situation where a signal handler is handler being executed in between a call to a failed function and the subsequent inspection of errno. Consequently, and, 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 that contain 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 in between a failed function call and the subsequent inspection of errno.

Code Block
bgColor#FFcccc
langc

#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;
}

...

Code Block
bgColor#ccccff
langc

#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;
}

...

section

Tool

Version

Checker

Description

Compass/ROSE

 

 

section

 

could

Could detect violations of this rule by looking for signal handlers that themselves call signal(). A violation is reported if the call fails and the handler therefore checks errno. A violation also exists if the signal handler modifies errno without first copying its value elsewhere.

Related Vulnerabilities

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

...

CERT C++ Secure Coding Standard: ERR32-CPP. Do not rely on indeterminate values of errno

ISO/IEC 9899:1999 Section 2011 Section 7.14.1.1, "The signal function"

Bibliography

...