Call only asynchronous-safe functions within signal handlers. This restriction applies to library functions as well as application-defined functions.
According to Section 7.14.1.1 of the C Rationale [ISO/IEC 2003]
When a signal occurs, the normal flow of control of a program is interrupted. If a signal occurs that is being trapped by a signal handler, that handler is invoked. When it is finished, execution continues at the point at which the signal occurred. This arrangement can cause problems if the signal handler invokes a library function that was being executed at the time of the signal.
Similarly, Section 7.14.1, paragraph 5 of C99 [ISO/IEC 9899:1999] states that if the signal occurs other than as the result of calling the abort
or raise
function, the behavior is undefined if
the signal handler calls any function in the standard library other than the
abort
function, the_Exit
function, or thesignal
function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler.
Many systems define an implementation-specific list of asynchronous-safe functions. In general, I/O functions are not safe to invoke inside signal handlers. Check your system's asynchronous-safe functions before using them in signal handlers.
Noncompliant Code Example
In this noncompliant code example, the program allocates a string on the heap and uses it to log messages in a loop. The program also registers the signal handler int_handler()
to handle the terminal interrupt signal SIGINT
. The int_handler()
function logs the last message, calls free()
, and exits.
#include <signal.h> #include <stdio.h> #include <stdlib.h> enum { MAXLINE = 1024 }; char *info = NULL; void log_message(void) { fprintf(stderr, info); } void handler(int signum) { log_message(); free(info); info = NULL; } int main(void) { if (signal(SIGINT, handler) == SIG_ERR) { /* Handle error */ } info = (char*)malloc(MAXLINE); if (info == NULL) { /* Handle Error */ } while (1) { /* Main loop program code */ log_message(); /* More program code */ } return 0; }
This program has four problems. The first is that it is unsafe to call the fprintf()
function from within a signal handler because the handler may be called when global data (such as stderr
) is in an inconsistent state. In general, it is not safe to invoke I/O functions within a signal handler.
The second problem is that the free()
function is also not [asynchronous-safe], and its invocation from within a signal handler is also a violation of this rule. If an interrupt signal is received during the free()
call in handler()
, the heap may be corrupted.
The third problem is if SIGINT
occurs after the call to free()
, resulting in the memory referenced by info
being freed twice. This is a violation of rules MEM31-C. Free dynamically allocated memory exactly once and SIG31-C. Do not access or modify shared objects in signal handlers.
The fourth and final problem is that the signal handler reads the variable info
, which is not declared to be of type volatile sig_atomic_t
. This is a violation of rule SIG31-C. Do not access or modify shared objects in signal handlers.
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 signal handler.
Asynchronous—signal-safe functions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All functions not in this table are considered to be unsafe with respect to signals. In the presence of signals, all functions defined by IEEE standard 1003.1-2001 behave as defined when called from or interrupted by a signal handler, with a single exception: when a signal interrupts an unsafe function and the signal handler calls an unsafe function, the behavior is undefined.
Note that while raise()
is on the list of asynchronous-safe functions, it is specifically covered by rule SIG33-C. Do not recursively invoke the raise() function.
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
Signal handlers should be as concise as possible, ideally, unconditionally setting a flag and returning. They may also call the _Exit()
function. Finally, they may call other functions provided that all implementations to which the code is ported guarantee that these functions are asynchronous-safe.
This example code achieves compliance with this rule by moving the final log message and call to free()
outside the signal handler.
#include <signal.h> #include <stdio.h> #include <stdlib.h> enum { MAXLINE = 1024 }; volatile sig_atomic_t eflag = 0; char *info = NULL; void log_message(void) { fprintf(stderr, info); } void handler(int signum) { eflag = 1; } int main(void) { if (signal(SIGINT, handler) == SIG_ERR) { /* Handle error */ } info = (char*)malloc(MAXLINE); if (info == NULL) { /* Handle error */ } while (!eflag) { /* Main loop program code */ log_message(); /* More program code */ } log_message(); free(info); info = NULL; return 0; }
Risk Assessment
Invoking functions that are not asynchronous-safe from within a signal handler may result in privilege escalation and other attacks.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
SIG30-C |
high |
likely |
medium |
P18 |
L1 |
Automated Detection
Tool |
Version |
Checker |
Description |
---|---|---|---|
Compass/ROSE |
|
|
can detect violations of the rule for single-file programs |
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.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C++ Secure Coding Standard: SIG30-CPP. Call only asynchronous-safe functions within signal handlers
ISO/IEC 9899:1999 Section 7.14, "Signal handling <signal.h>
"
MITRE CWE: CWE ID 479, "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"
[Open Group 2004] longjmp
[OpenBSD] signal()
Man Page
[Zalewski 2001]
11. Signals (SIG) SIG31-C. Do not access or modify shared objects in signal handlers