Skip to end of metadata
Go to start of metadata

According to the C Standard [ISO/IEC 9899:2011], the behavior of 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 133.

A signal handler is allowed to call signal(); 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; if signal() fails, the value of errno is indeterminate.

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, 7.14.1.1, paragraph 5, makes a special exception for errno in this case, 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:

#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 */
  }
}

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

  /* 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

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

#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 */
  }

  /* Main code loop */

  return EXIT_SUCCESS;
}

Noncompliant Code Example (POSIX)

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 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. As a result, it can cause incorrect error handling if executed between a failed function call and the subsequent inspection of errno:

#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)

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

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

void reaper(int signum) {
  errno_t 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 EXIT_SUCCESS;
}

Risk Assessment

Referencing indeterminate values of errno is undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ERR32-C

Low

Unlikely

Low

P3

L3

Automated Detection

Tool

Version

Checker

Description

Axivion Bauhaus Suite

6.9.0

CertC-ERR32
Compass/ROSE



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

Coverity
2017.07

MISRA C 2012 Rule 22.8

MISRA C 2012 Rule 22.9

MISRA C 2012 Rule 22.10

Implemented
LDRA tool suite
9.7.1
44 SEnhanced enforcement
Parasoft C/C++test10.4.2CERT_C-ERR32-a

Properly use errno value

Polyspace Bug Finder

R2019a

CERT C: Rule ERR32-CChecks for misuse of errno in a signal handler (rule fully covered)
PRQA QA-C

9.5

2031

Related Vulnerabilities

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

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding StandardSIG30-C. Call only asynchronous-safe functions within signal handlersPrior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardSIG31-C. Do not access shared objects in signal handlersPrior to 2018-01-12: CERT: Unspecified Relationship

Bibliography

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



13 Comments

  1. The compliant code example violates SIG32, which dictates that only volatile sig_atomic_t variables should be accessed from within a signal handler.

    The non-compliant code example violates both SIG32 and SIG31, too.

    Right now this rule looks to me more like it belongs in the SIG section. I think it should be transferred there OR a coding example (compliant or non-compliant) that does not involve signals should be added to this rule.

    1. I agree; this rule belongs in the SIG section.

  2. I still see a problem with the CCE... what happens when the signal() inside of handler fails and trashes errno for some other function?

    int main(void) {
      /* register handler() */
      if(somefunction() != 0) {
        /**** SIGNAL EVENT ****/
        perror("SomeFunction Error:");
      }
    }
    

    The perror() might print the wrong message if errno was changed by the signal handler...

    1. I have fixed this by making the handler abort if the signal() call fails.

  3. Isn't it possible that in this reference to errno in the compliant solution for POSIX:

    void reaper(int signum) {
      int save_errno = errno;
    

    that the value of errno is indeterminate? if it is, how would this effect the execution of this code?

    1. The value of errno at that point would only be indeterminate if a signal handler has previously been executed which did not save and restore errno (and which contains code that might alter errno).

      Would it help if the text said "POSIX applications can avoid this problem by ensuring that all signal handlers ..."?

  4. Is it really safe and compliant to save errno when entering a signal handler and then restore errno when leaving the handler; e.g. do something like this :

    void handle(int signum)
    {
      int saved_errno = errno;
    
      /* Handler code that may alter errno */
    
      /* Restore original errno before leaving */
      errno = saved_errno;
    } // (cleaned up spacing ~DS)
    

    Not that sure since only volatile sig_atomic_t global variables can be safely set from a signal handler and I'm not sure errno is required to be volatile sig_atomic_t by C99 or POSIX... If it's not, what's the safe solution ?

    1. Good question!

      1999 Section 7.14.1.1, "The signal function", paragraph 5 sez:

      If the signal occurs other than as the result of calling the abort or
      raise function, the behavior is undefined if the signal handler refers to any object with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a ,SIG_ERR return, the value of errno is indeterminate.

      The POSIX standard contains a nearly identical paragraph.

      I see nothing about a 'special exception' for working with errno in a signal handler. At the very least we need to update the reference.

      1. The signal() page in POSIX is basically a legacy page which closely matches the C Standard. POSIX applications should not use signal(), they should use sigaction().

        The rules for signal handling when sigaction() is used are in XSH 2.4.3, which says (in the new 2008 revision):

        Operations which obtain the value of errno and operations which assign a value to errno shall be async-signal-safe.

        1. According to POSIX, "The symbol errno, defined by including the <errno.h> header, expands to a modifiable lvalue of type int"

          But there is no requirement for errno to be volatile sig_atomic_t.

          On many plateforms, sig_atomic_t is actually an int; so on those plateforms errno is sig_atomic_t. However, this is still not "volatile sig_atomic_t" as required for global variables set in signal handlers ... So what ?

          1. Right. Hence the need for the explicit requirement about errno in XSH 2.4.3 that I quoted above.

            This was added in the latest revision precisely because it was known that saving and restoring errno in signal handlers is recommended practice, but it was not previously clear whether POSIX allowed it.

  5. The use of errno_t in the last Compliant Solution (POSIX) is not strictly conforming (i.e., it's unportable). First, errno_t is not a POSIX type (the latest POSIX, SUSv4, is based on C99, not C11). Second, even when POSIX adopts C11 (unless it mandates Annex K, which is unlikely), errno_t will be a conditionally defined type.

    1.  

      Yup.  DCL09-C. Declare functions that return errno with a return type of errno_t describes the use of errno_t.  There is a very long sequence of code that can be written to use errno_t if conditionally defined or define it if it is not, e.g.: 

       
      #ifdef __STDC_LIB_EXT1__
      #define __STDC_WANT_LIB_EXT1__ 1
      #include <errno.h>
      #elif
        typedef int errno_t;
      #endif
       

      Doing this for each example would be a distraction, but in a real program this would all be performed in a single header file and errno_t could be used freely.