Simultaneous signals oddity

David Wragg (dpw@doc.ic.ac.uk)
26 Nov 1999 02:15:19 +0000


In arch/i386/kernel/signal.c:do_signal() (from 2.3.29, but it hasn't
changed in a long while)

/* Whee! Actually deliver the signal. */
handle_signal(signr, ka, &info, oldset, regs);
return 1;

And so, with the handler set up, we leave the kernel. But what if
current has other signals pending -- shouldn't we set up handlers for
them too? We don't: we return to user-space with current->sigpending
potentially still set.

I have written a program, below, which uses this loop-hole to delay a
signal by on average half a clock tick. In general, this seems pretty
harmless; most code will handle the next pending signal at the
sigreturn (if not before), and the clock tick comes around
eventually. But there's nothing to stop SIGKILL being delayed in the
same fashion, although it would be very hard to reproduce.

Is this intentional? Even if it is, I think we ought to make sure that
unblockable signals always get delived "immediately", i.e. by having
them take precedence in dequeue_signal().

David Wragg

/* Try to delay delivery of a signal */

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>

#define RDTSC(x) __asm__ __volatile__ ( "rdtsc" \
:"=a" (((unsigned long*)&x)[0]), \
"=d" (((unsigned long*)&x)[1]))

long long usr1time;
long long usr2time;

jmp_buf jb;

void handler1(int s)
{
RDTSC(usr1time);

/* Avoid re-entering the kernel for as long as possible */
for(;;)
;
}

void handler2(int s)
{
RDTSC(usr2time);

longjmp(jb, 1);
}

int main(void)
{
if (setjmp(jb) == 0) {
/* Make two signals become deliverable simultaneously */
sigset_t ss;
struct sigaction sa;

sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);

sigprocmask(SIG_BLOCK, &ss, NULL);

sa.sa_handler = handler1;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);

sa.sa_handler = handler2;
sigaction(SIGUSR2, &sa, NULL);

kill(getpid(), SIGUSR1);
kill(getpid(), SIGUSR2);

sigprocmask(SIG_UNBLOCK, &ss, NULL);
}
else {
long diff = usr2time - usr1time;

if (diff < 0)
diff = -diff;

printf("Delayed a signal by %ld cycles!\n", diff);
}

return 0;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/