Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can there be a race between signalfd and sigaction?

The classical way to specify a handler for a certain signal is via sigaction. Linux additionally provides the signalfd functionality, where we can connect signals to a file descriptor and then apply select/(e)poll to that descriptor, which perfectly fits the concept of many event loop-driven systems.

I am wondering what happens / should happen when both mechanisms collide. Can there be race conditions? On the signalfd manpage (http://man7.org/linux/man-pages/man2/signalfd.2.html) we read:

Normally, the set of signals to be received via the file descriptor should be blocked using sigprocmask(2), to prevent the signals being handled according to their default dispositions.

So, it says "normally" we use the signal mask in order to prevent the (default) handler from processing a signal. It does not say that we have to block that signal when we have a file descriptor connected to it. Unfortunately, the man page does not specify what happens when we don't block the signal.

This looks like poorly defined behavior. I do not believe that this is actually not well-defined and am wondering if anyone here knows i) were I can find a detailed specification about how the system should behave or ii) how it behaves.

What I am specifically interested in, is this order of execution:

  1. signalfd for a certain signal, including blockage of this signal
  2. unblockage of this signal
  3. sigaction for this signal (default handler or custom handler)

Is this undefined behavior or is there a standard/specification for what must happen? Does the handler always take precedence over the file descriptor? Is the handler called and the file descriptor fires off an event? Does setting sigaction change the signal mask, rendering step (2) unnecessary?

I could try to derive the actual behavior from systematic tests involving actual code. However, I of course prefer to find a detailed piece of documentation and consider that I was not able to find the right reference myself.

like image 702
Dr. Jan-Philip Gehrcke Avatar asked Nov 26 '13 21:11

Dr. Jan-Philip Gehrcke


People also ask

What is Signalfd?

signalfd() creates a file descriptor that can be used to accept signals targeted at the caller. This provides an alternative to the use of a signal handler or sigwaitinfo(2), and has the advantage that the file descriptor may be monitored by select(2), poll(2), and epoll(7).

How do signal handlers work?

A signal handler is a function which is called by the target environment when the corresponding signal occurs. The target environment suspends execution of the program until the signal handler returns or calls longjmp() . Signal handlers can be set with signal() or sigaction() .


1 Answers

signalfd behaves identically to sigwaitinfo, except you get to access the information via a file descriptor. This means that a signalfd receives signals synchronously, and signal handlers (or whichever is the default disposition) are called first.

Source: TLPI chapters 22.10 and 22.11 (M. Kerrisk).

The behavior is well-defined, but not necessarily what one would expect, and the manpage is worded rather badly. Saying "normally... should" suggests that either you don't really have to, or worse that the author isn't quite sure.
If you want it to work "properly", i.e. the way you'd normally expect (see, I used "normally" too), you do have to block the signals. Otherwise, the signal will be available via the file descriptor, but a handler will still be called first (which is entirely legitimate, but most people would probably consider this "weird behavior").

Thus, there exist two different race conditions. One condition is the one you ask about, but although the behavior is a bit unexpected, it is well-defined and (kind of) documented, and if you think about it, then it isn't strictly a race condition. Rather it is a kind of "double delivery".
The other race condition is the possibility of a signal arriving after you created the signalfd but before you blocked the signal. It is very unlikely, but in principle, this could happen. Luckily, the solution is easy, you can block the signal first and then create the file descriptor (which will then immediately be ready if a signal arrived in between).

The sequence of commands that you named (create file descriptor, unblock, then sigaction) has a similar race condition. You should first install a handler and then unblock, or a signal may be delivered before the handler is there. But that's irrespective of signalfd. The file descriptor could still be used to read the signal in any case, but the default disposition might kill the process if a signal arrives between unblocking and installing the handler.

like image 66
Damon Avatar answered Oct 06 '22 00:10

Damon