Call only asynchronous-safe functions within signal handlers.
According to Section 7.14.1.1 of the C Rationale [[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 could 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
orraise
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 volatilesig_atomic_t
, or the signal handler calls any function in the standard library other than theabort
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.
Non-Compliant Code Example
In this non-compliant code example, storage is dynamically allocated to hold a copy of a string. A string literal is copied into the allocated memory, which is then printed and the memory freed. The program also registers the signal handler int_handler()
to handle the terminal interrupt signal SIGINT
. The int_handler()
function calls free()
then exits.
#include <signal.h> char *foo; void int_handler() { free(foo); _Exit(0); } int main(void) { foo = (char *)malloc(sizeof("Hello World.")); if (foo == NULL) { /* handle error condition */ } signal(SIGINT, int_handler); strcpy(foo, "Hello World."); puts(foo); free(foo); return 0; }
This program has three potential problems. The first is that the free()
function is not asynchronous-safe, and its invocation from within a signal handler is a violation of this rule. If an interrupt signal is received during the free()
call in main()
, the heap may be corrupted.
The second problem is if SIGINT
occurs after the call to free()
, resulting in the memory referenced by foo()
being freed twice. This is a violation of MEM31-C. Free dynamically allocated memory exactly once and SIG31-C. Do not access or modify shared objects in signal handlers.
The third problem is that the signal handler reads the variable foo
, which is not declared to be of type volatile sig_atomic_t
. This is also a violation of SIG31-C. Do not access or modify shared objects in signal handlers.
The _Exit()
function called from within the int_handler()
signal handler causes immediate program termination and is asynchronous-safe, whereas exit()
may call cleanup routines first, and consequently is not asynchronous-safe. If it is important that your application invoke handlers registered by the atexit()
function to perform cleanup actions before exiting, you may wish to set a flag (of type volatile sig_atomic_t
) from the signal handler indicating the program should exit and return (see ERR04-A. Choose an appropriate termination strategy).
Implementation Details
POSIX
The following table from the the Open Group Base Specifications [[Open Group 04]] defines a set of functions that are asynchronous-signal-safe. Applications may consequently invoke them, without restriction, from signal-catching 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-catching function, with a single exception: when a signal interrupts an unsafe function and the signal-catching function 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 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 signal-catching functions.
The OpenBSD signal()
man page also says
A few other functions are signal race safe in OpenBSD but probably not on other systems: snprintf() Safe. vsnprintf() Safe. syslog_r() Safe if the syslog_data struct is initialized as a local variable.
Note that, 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.
Compliant Solution
Signal handlers should be as concise as possible, ideally unconditionally setting a flag and returning. They may also call the _Exit()
function.
#include <signal.h> void int_handler() { _Exit(0); } int main(void) { char *foo = (char *)malloc(sizeof("Hello World.")); if (foo == NULL) { /* handle error condition */ } signal(SIGINT, int_handler); strcpy(foo, "Hello World."); puts(foo); free(foo); 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 |
3 (high) |
3 (likely) |
1 (high) |
P9 |
L2 |
Automated Detection
The tool Compass Rose can detect violations of the rule for single-file programs.
Related Vulnerabilities
For an overview of some software vulnerabilities, see Zalewski's paper on understanding, exploiting, and preventing signal-handling related vulnerabilities [[Zalewski 01]]. 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.
References
[[Dowd 06]] Chapter 13, "Synchronization and State"
[[ISO/IEC 03]] Section 5.2.3, "Signals and interrupts"
[[ISO/IEC 9899-1999]] Section 7.14, "Signal handling <signal.h>"
[[Open Group 04]] longjmp
[[OpenBSD]] signal()
Man Page
[[Zalewski 01]]
SIG02-A. Avoid using signals to implement normal functionality 11. Signals (SIG) SIG31-C. Do not access or modify shared objects in signal handlers