Only call asynchronous-safe functions within signal handlers.
According to the "Signals and Interrupts" section of the C99 Rationale:
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. Since library functions are not guaranteed to be reentrant, they should not be called from a signal handler that returns.
Implementation Details
The OpenBSD signal()
man page identifies functions that are either reentrant or not interruptible by signals and are asynchronous-signal safe. Applications may therefore invoke them, without restriction, from signal-catching functions.
Non-Compliant Code Example
In this non-compliant code example, main()
invokes the malloc()
function to allocated space to copy a string. The 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
.
Unfortunately, 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 or after the free()
call in main()
, the heap may be corrupted.
#include <signal.h> char *foo; void int_handler() { free(foo); _Exit(0); } int main(void) { foo = malloc(sizeof("Hello World.")); if (foo == NULL) { /* handle error condition */ } signal(SIGINT, int_handler); strcpy(foo, "Hello World."); puts(foo); free(foo); return 0; }
The _Exit()
function called from within the int_handler()
signal handler causes immediate program termination, and is async-safe, whereas exit()
may call cleanup routines first, and is consequently not async-safe.
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> char *foo; void int_handler() { _Exit(0); } int main(void) { foo = 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 async-safe from within a signal handler may result in privilege escalation and other attacks. 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.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
SIG30-C |
3 (high) |
3 (likely) |
1 (high) |
P9 |
L2 |
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:TC2]] Section 7.14, "Signal handling <signal.h>"
[[Open Group 04]] longjmp
[OpenBSD] signal()
Man Page
[[Zalewski 01]]