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
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.
Non-Compliant Code Example
In this non-compliant 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) { signal(SIGINT, handler); info = (char*)malloc(MAXLINE); while (1) { /* main loop program code */ log_message(); /* more program code */ } return 0; }
This program has four potential problems. The first is that the log_message()
function calls fprintf()
, which is an unsafe function to call from within a signal handler, because the handler might have been called when global data (such as stderr
) was in an inconsistent state. In general, standard I/O is never safe to invoke 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 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 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 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 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 list 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 functions listed above as 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) { signal(SIGINT, handler); info = (char*)malloc(MAXLINE); 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 |
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