Accessing or modifying shared objects in signal handlers can lead to race conditions, opening up security holes.  The exception to this rule is the ability to read and write to variables of {{volatile sig_atomic_t}}.  The type of {{sig_atomic_t}} is [implementation-defined|BB. Definitions#implementation-defined behavior], although there are bounding constraints. Only integer values from {{0}} through {{127}} can be assigned to a variable of type {{sig_atomic_t}} to be fully portable.  The need for the {{volatile}} keyword is described in \[[DCL34-C. Use volatile for data that cannot be cached]\].

According to the "Signals and Interrupts" section of the \[[C99 Rationale|AA. C References#ISO/IEC 03]\], other than calling a limited, prescribed set of library functions:

The C89 Committee concluded that about the only thing a strictly conforming program can do in a signal handler is to assign a value to a volatile static variable which can be written uninterruptedly and promptly return.

However, this issue was discussed at the April 2008 meeting of ISO/IEC WG14 and it was agreed that there are no known implementations in which it would be an error to read a value from a volatile static variable and the original intent of the committee was that both reading and writing variables of volatile sig_atomic_t would be strictly conforming.

The signal handler may also call 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.  This may be necessary to ensure that the handler persists (see \[[SIG01-A. Understand implementation-specific details regarding signal handler persistence]\]).

Non-Compliant Code Example

In this non-compliant code example, err_msg is updated to indicate that the SIGINT signal that was delivered. Undefined behavior will occur if a SIGINT is generated before the allocation completes.

#include <signal.h>
#include <stdlib.h>
#include <string.h>

char *err_msg;
enum { MAX_MSG_SIZE = 24 };

void handler(int signum) {
  strcpy(err_msg, "SIGINT encountered.");
}

int main(void) {

  signal(SIGINT, handler);

  err_msg = (char *)malloc(MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* handle error condition */
  }
  strcpy(err_msg, "No errors yet.");

  /* main code loop */

  return 0;
}

Compliant Solution

Portably, signal handlers can only unconditionally get or set a flag of type volatile sig_atomic_t and return.

#include <signal.h>
#include <stdlib.h>
#include <string.h>

volatile sig_atomic_t e_flag = 0;
volatile sig_atomic_t e_value = 1;

void handler(int signum) {
  e_flag = e_value;
}

int main(void) {
  char *err_msg;
  enum { MAX_MSG_SIZE = 24 };
  signal(SIGINT, handler);

  err_msg = (char *)malloc(MAX_MSG_SIZE);
  if (err_msg == NULL) {
    /* handle error condition */
  }

  strcpy(err_msg, "No errors yet.");

  /* main code loop */

  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }
  return 0;
}

Risk Assessment

Depending on the code, this could lead to any number of attacks, many of which could give root access. For an overview of some software vulnerabilities, see \[[Zalewski 06|AA. C References#Zalewski 06]\].

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SIG31-C

3 (high)

3 (likely)

1 (high)

P9

L2

Automated Detection

The tool Compass Rose can detect violations of the recommendation for single-file programs.

Related Vulnerabilities

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

References

\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 13, Synchronization and State
\[[ISO/IEC 03|AA. C References#ISO/IEC 03]\] "Signals and Interrupts"
\[[Open Group 04|AA. C References#Open Group 04]\] [longjmp|http://www.opengroup.org/onlinepubs/000095399/functions/longjmp.html]
\[[OpenBSD|AA. C References#OpenBSD]\] [{{signal()}} Man Page|http://www.openbsd.org/cgi-bin/man.cgi?query=signal]
\[[Zalewski 06|AA. C References#Zalewski 06]\] [http://lcamtuf.coredump.cx/signals.txt]


      11. Signals (SIG)       SIG32-C. Do not call longjmp() from inside a signal handler