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|AA. Bibliography#ISO/IEC 03]\] |
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|AA. Bibliography#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|BB. Definitions#undefined behavior] 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.
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|AA. Bibliography#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 {{main()}}, 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.
The following table from the the Open Group Base Specifications \[[Open Group 2004|AA. Bibliography#Open Group 04]\], 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.
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).
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; } |
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 |
Tool |
Version |
Checker |
Description |
||
---|---|---|---|---|---|
|
|
|
|
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|AA. Bibliography#Zalewski 01]\]. [VU #834865|http://www.kb.cert.org/vuls/id/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.
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"
\[[Dowd 2006|AA. Bibliography#Dowd 06]\] Chapter 13, "Synchronization and State" \[[ISO/IEC 2003|AA. Bibliography#ISO/IEC 03]\] Section 5.2.3, "Signals and interrupts" \[[Open Group 2004|AA. Bibliography#Open Group 04]\] [longjmp|http://www.opengroup.org/onlinepubs/000095399/functions/longjmp.html] \[[OpenBSD|AA. Bibliography#OpenBSD]\] [{{signal()}} Man Page|http://www.openbsd.org/cgi-bin/man.cgi?query=signal] \[[Zalewski 2001|AA. Bibliography#Zalewski 01]\] |
11. Signals (SIG) SIG31-C. Do not access or modify shared objects in signal handlers