I am just figuring out whether I can call a non-async-safe function in a signal handler.
Quotes from Linux man page signal(7):
If a signal interrupts the execution of an unsafe function, and handler calls an unsafe function, then the behavior of the program is undefined.
SUSv3 notes that all functions not listed in Table 21-1 (list of async-safe functions) are considered to be unsafe with respect to signals, but points out that a function is unsafe only when invocation of a signal handler interrupts the execution of an unsafe function, and the handler itself also calls an unsafe function.
crypt(3)
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handler;
sigaction(SIGINT, &sa, NULL);
printf()
main()
printf()
printf()
crypt()
Yes indeed, it is unsafe to call a non-async-signal-safe function from inside a signal handler in all cases (unless you dive into your implementation code -e.g. libc
and perhaps your compiler generating code for it); then you could perhaps prove that calling such an such function is in fact safe; but such a proof might be impossible or take months or years of your time, even with the help of static analyzers à la Frama-C, ... and require studying all the implementation details.
Concretely, it is probable that crypt
is internally calling malloc
(for some internal data, etc...). And the standard malloc
function has obviously some global state (e.g. the vector of linked lists of buckets related to previously free
-d memory zone, to be reused by future calls to malloc
).
Remember that Unix signals can occur at every machine instruction (not only at C sequence points, which have some well defined semantics). With bad luck, a signal might occur in the few machine instructions which are updating the global state of malloc
. Then future calls to malloc
-e.g. indirectly from your signal handler, might break havoc (i.e. undefined behavior). Such a misfortune might be unlikely (but evaluating its probability is practically impossible), but you should code against it.
Details are heavily implementation specific, depending on your compiler and optimization flags, libc
, kernel, processor architecture, etc...
You might not care about async-signal-safe functions, by betting that disaster won't happen. This might be acceptable for debugging purposes (e.g. very often, but not always, a printf
inside a signal handler would practically work most of the time; and the GCC compiler is internally using its "async-unsafe" libbacktrace library in signal handlers) for code wrapped with #ifndef NDEBUG
, but it won't be good for production code; if you really have to add such code in a handler, mention in a comment that you know that you are doing wrongly a call to a non-async-signal-safe function, and be prepared to be cursed by future colleagues working on the same code base.
A typical trick to handle such situations is to simply set a volatile sig_atomic_t
flag in the signal handler (read POSIX signal.h documentation) and check that flag in some safe place in some loop -outside of the handler-, or to write(2) one -or a few- bytes to a pipe(7) previously setup at application initialization, and have the read end of that pipe be periodically poll(2)-ed and later read by your event loop -or some other thread-).
(I've taken malloc
as an example, but you could think of other widely used non-async-signal-safe functions, or even implementation specific routines, e.g. 64 bits arithmetic on a 32 bits processor, etc...).