...
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 freedthe 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 sleeps for a short time, then logs the last message, calls free()
then , and finally exits.
Code Block | ||
---|---|---|
| ||
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> enum { MAXLINE = 1024 }; char *fooinfo = NULL; void int_handler(log_message() { fprintf( stderr, info); } void handler(int signum) { freesleep(foo1); _Exit(0log_message(); free( info); } int main(void) { foosignal(SIGINT, handler); info = (char *) malloc(sizeof("Hello World."))MAXLINE); ifwhile (foo == NULL1) { /* main handleloop errorprogram conditioncode */ } signal(SIGINT, int_handlerlog_message(); strcpy(foo, "Hello World."); puts(foo); free(foo); /* more program code */ } return 0; } |
This program has three 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...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.
Wiki Markup |
---|
The second problem is that the {{free()}} function is also not \[[asynchronous-safe|AA. C References#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 second third problem is if SIGINT
occurs after the call to free()
, resulting in the memory referenced by foo()
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 third fourth and final problem is that the signal handler reads the variable foo
info
, 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
...
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. This signal handler still calls the sleep()
function, which is asynchronous-safe.
Code Block | ||
---|---|---|
| ||
#include <signal.h> #include <stdio.h> void int_handler(#include <stdlib.h> #include <unistd.h> enum { MAXLINE = 1024 }; volatile sig_atomic_t eflag = 0; char *info = NULL; void log_message() { fprintf( stderr, info); } void handler(int signum) { eflag _Exit(0= 1; sleep(1); } int main(void) { signal(SIGINT, handler); char *fooinfo = (char *) malloc(sizeof("Hello World."))MAXLINE); ifwhile (foo == NULL!eflag) { /* handlemain loop errorprogram conditioncode */ } signal(SIGINT, int_handlerlog_message(); strcpy(foo, "Hello World."); puts(foo /* more program code */ } log_message(); free(foo info); return 0; } |
Risk Assessment
...