In arch/i386/signal.c, in the do_signal() function, it calls
get_signal_to_deliver() which returns the signal number to deliver (along
with siginfo). get_signal_to_deliver() grabs and releases the lock, so
the signal handler lock is not held in do_signal(). Then the do_signal()
calls handle_signal(), which uses the signal number to extract the
sa_handler, etc.
Since no lock is held, it seems like another thread with the same
signal handler set can come in and call sigaction(), it can change
sa_handler between the call to get_signal_to_deliver() and fetching the
value of sa_handler. If the sigaction() call set it to SIG_IGN, SIG_DFL,
or some other fundamental change, that bad things can happen.
The patch:
You have to get the sigaction information that will be delivered while
holding sighand->siglock in get_signal_to_deliver().
In 2.4, it can be fixed per-arch and requires no change to the
arch-independent code because the arch fetches the signal with
dequeue_signal() and does all the checking.
The test app:
The program below has three threads that share signal handlers. Thread
1 changes the signal handler for a signal from a handler to SIG_IGN and
back. Thread 0 sends signals to thread 3, which just receives them.
What I believe is happening is that thread 1 changes the signal handler
in the process of thread 3 receiving the signal, between the time that
thread 3 fetches the signal info using get_signal_to_deliver() and
actually delivers the signal with handle_signal().
Although the program is obvously an extreme case, it seems like any
time you set the handler value of a signal to SIG_IGN or SIG_DFL, you can
have this happen. Changing signal attributes might also cause problems,
although I am not so sure about that.
(akpm: this test app segv'd on SMP within milliseconds for me)