Versions Compared

Key

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

Call only asynchronous-safe functions within signal handlers. For strictly conforming programs, only the C Standard Library standard library functions abort(), _Exit(), and signal() can be called from within a signal handler. 

Section 7.14.1.1, para. paragraph 5, of the C standard Standard [ISO/IEC 9899:2011], states that if the signal occurs other than as the result of calling the abort() or raise() function, the behavior is undefined if

...

In this noncompliant example, the C Standard Library standard library function fprintf() is called from the signal handler handler via the function log_message(). The function free() is  also is also not asynchronous-safe, and its invocation from within a signal handler is also a violation of this rule.

...

Signal handlers should be as concise as possiblepossible—ideally, ideally, unconditionally setting a flag and returning. This compliant solution sets a flag of type volatile sig_atomic_t and returns; the log_message() and free() functions are called directly from main().

...

Code Block
bgColor#ffcccc
langc
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>

enum { MAXLINE = 1024 };
static jmp_buf env;

void handler(int signum) {
  longjmp(env, 1); /* violation */
}

void log_message(char *info1, char *info2) {
  static char *buf = NULL;
  static size_t bufsize;
  char buf0[MAXLINE];

  if (buf == NULL) {
    buf = buf0;
    bufsize = sizeof(buf0);
  }

  /*
   *  Try to fit a message into buf, else re-allocatereallocate
   *  it on the heap and then log the message.
   */

/*** VULNERABILITY IF SIGINT RAISED HERE ***/

  if (buf == buf0) {
    buf = NULL;
  }
}

int main(void) {
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* Handle error */
  }
  char *info1;
  char *info2;

  /* info1 and info2 are set by user input here */

  if (setjmp(env) == 0) {
    while (1) {
      /* Main loop program code */
      log_message(info1, info2);
      /* More program code */
    }
  }
  else {
    log_message(info1, info2);
  }

  return 0;
}

...

Code Block
bgColor#ccccff
langc
#include <signal.h>

void log_msg(int signum) {
  /* Log error message in some asynchronous-safe manner */
}

void handler(int signum) {
  /* Do some handling specific to SIGINT */
  log_msg(SIGUSR1);
}

int main(void) {
  if (signal(SIGUSR1, log_msg) == SIG_ERR) {
    /* Handle error */
  }
  if (signal(SIGINT, handler) == SIG_ERR) {
    /* handleHandle error */
  }

  /* program code */
  if (raise(SIGINT) != 0) {
    /* Handle error */
  }
  /* More code */

  return 0;
}

...

The POSIX standard is contradictory regarding raise() in signal handlers. The POSIX standard [Open Group 2004] prohibits signal handlers installed using signal() from calling the raise() function if the signal occurs as the result of calling the raise()kill()pthread_kill(), or sigqueue() functions. However, it also requires that allows the raise() function may to be safely called within any signal handler. Consequently, it is not clear whether it is safe for POSIX applications to call raise() in signal handlers installed using signal(), but it is safe to call raise() in signal handlers installed using sigaction()

 In this non-compliant noncompliant code example, the signal handlers are installed using signal(), and raise() is called inside the signal handler.

Code Block
bgColor#ffcccc
langc
#include <signal.h>

void log_msg(int signum) {
  /* Log error message */
}

void handler(int signum) {
  /* Do some handling specific to SIGINT */
  if (raise(SIGUSR1) != 0) { /* violation */
    /* Handle error */
  }
}

int main(void) {

  signal(SIGUSR1, log_msg);
  signal(SIGINT, handler);
   
  /* program code */
  if (raise(SIGINT) != 0) {
    /* Handle error */
  }
  /* More code */

  return 0;
}

Implementation Details

POSIX

The following table from the the Open Group Base Specifications [Open Group 2004] defines a set of functions that are asynchronous-signal-safe. Applications may invoke these functions, without restriction, from a signal handler.

...

Note that although raise() is on the list of asynchronous-safe functions, it should not be called within a signal handler if the signal occurs as a result of of the abort() or raise() function.

Section 7.14.1.1, para. paragraph 4, of the C standard Standard [ISO/IEC 9899:2011] states:

  If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.

 (See also undefined behavior 131 of in Annex J.) 

OpenBSD

The OpenBSD signal() man page identifies functions that are asynchronous-signal safe. Applications may consequently invoke them, without restriction, from a signal handler. 

The OpenBSD signal() manual page lists a few additional functions that are asynchronous-safe in OpenBSD but "probably not on other systems," including snprintf()vsnprintf(), and syslog_r()( but only when the syslog_data struct is initialized as a local variable).

Compliant Solution (POSIX)

In this compliant solution, the signal handlers are installed using sigaction(), and so it is safe to use raise() within the signal handler.

...

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

...

Tool

Version

Checker

Description

Compass/ROSE  Can detect violations of the rule for single-file programs.

LDRA tool suite

Include Page
LDRA_V
LDRA_V

88 D
89 D 

Fully implemented.

Splint

Include Page
Splint_V
Splint_V

 

 

Related Vulnerabilities

For an overview of software vulnerabilities resulting from improper signal handling, see Zalewski's paper on understanding, exploiting, and preventing signal-handling-related vulnerabilities [Zalewski 2001]. VU #834865 describes a vulnerability resulting from a violation of this rule.

Another notable case where using the longjmp() function in a signal handler caused a serious vulnerability is wu-ftpd 2.4 [Greenman 1997]. The effective user ID is set to zero 0 in one signal handler. If a second signal interrupts the first, a call is made to longjmp(), returning the program to the main thread but without lowering the user's privileges. These escalated privileges can be used for further exploitation.

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

Related Guidelines

...

...

ISO/IEC TR 17961 (Draft)Calling functions in the C Standard Library other than abort, _Exit, and signal from within a signal handler [asyncsig]
MITRE CWE

...

...

...

Unsafe function call from a signal handler

...

Bibliography

[Dowd 2006]Chapter 13, "Synchronization and State"
[ISO/IEC 2003]Section 5.2.3, "Signals and

...

Interrupts"
Section 7.14.1.1, "The signal Function"
[ISO/IEC 9899:2011]Section 7.14, "Signal Handling <signal.h>"
[Open Group 2004]longjmp()
[OpenBSD]signal() Man Page
[Zalewski 2001]"Delivering Signals for Fun and Profit"