...
In this non-compliant code example, main()
invokes the malloc()
function to allocate space to copy storage is dynamically allocated to hold a copy of a string. The 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
.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, which also calls free()
.
Code Block | ||
---|---|---|
| ||
#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 two 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 also 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 is consequently not asynchronous-safe.
...
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | |
|
| |
|
|
All functions not in the above this table are considered to be unsafe with respect to signals. In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001 shall 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.
...
Code Block | ||
---|---|---|
| ||
#include <signal.h> char *foo; 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; } |
...
The tool Compass Rose can detect violations of the rule for single-file programs.
Mitigation Strategies
Static Analysis
Compliance with this rule can be checked using structural static analysis checkers using the following algorithm:
...
.
References
Wiki Markup |
---|
\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 13, "Synchronization and State" \[[ISO/IEC 03|AA. C References#ISO/IEC 03]\] Section 5.2.3, "Signals and interrupts" \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.14, "Signal handling <signal.h>" \[[Open Group 04|AA. C References#Open Group 04]\] [longjmp|http://www.opengroup.org/onlinepubs/000095399/functions/longjmp.html] \[OpenBSD\] [{{signal()}} Man Page|http://www.openbsd.org/cgi-bin/man.cgi?query=signal] \[[Zalewski 01|AA. C References#Zalewski 01]\] |
...