Skip to content

Commit

Permalink
Prioritize synchronous signals over 'normal' signals
Browse files Browse the repository at this point in the history
This makes sure that we pick the synchronous signals caused by a
processor fault over any pending regular asynchronous signals sent to
use by [t]kill().

This is not strictly required semantics, but it makes it _much_ easier
for programs like Wine that expect to find the fault information in the
signal stack.

Without this, if a non-synchronous signal gets picked first, the delayed
asynchronous signal will have its signal context pointing to the new
signal invocation, rather than the instruction that caused the SIGSEGV
or SIGBUS in the first place.

This is not all that pretty, and we're discussing making the synchronous
signals more explicit rather than have these kinds of implicit
preferences of SIGSEGV and friends.  See for example

	http://bugzilla.kernel.org/show_bug.cgi?id=15395

for some of the discussion.  But in the meantime this is a simple and
fairly straightforward work-around, and the whole

	if (x & Y)
		x &= Y;

thing can be compiled into (and gcc does do it) just three instructions:

	movq    %rdx, %rax
	andl    $Y, %eax
	cmovne  %rax, %rdx

so it is at least a simple solution to a subtle issue.

Reported-and-tested-by: Pavel Vilim <[email protected]>
Acked-by: Oleg Nesterov <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
torvalds committed Mar 4, 2010
1 parent eaa5eec commit a27341c
Showing 1 changed file with 30 additions and 13 deletions.
43 changes: 30 additions & 13 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,33 +159,50 @@ void recalc_sigpending(void)

/* Given the mask, find the first available signal that should be serviced. */

#define SYNCHRONOUS_MASK \
(sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \
sigmask(SIGTRAP) | sigmask(SIGFPE))

int next_signal(struct sigpending *pending, sigset_t *mask)
{
unsigned long i, *s, *m, x;
int sig = 0;

s = pending->signal.sig;
m = mask->sig;

/*
* Handle the first word specially: it contains the
* synchronous signals that need to be dequeued first.
*/
x = *s &~ *m;
if (x) {
if (x & SYNCHRONOUS_MASK)
x &= SYNCHRONOUS_MASK;
sig = ffz(~x) + 1;
return sig;
}

switch (_NSIG_WORDS) {
default:
for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
if ((x = *s &~ *m) != 0) {
sig = ffz(~x) + i*_NSIG_BPW + 1;
break;
}
for (i = 1; i < _NSIG_WORDS; ++i) {
x = *++s &~ *++m;
if (!x)
continue;
sig = ffz(~x) + i*_NSIG_BPW + 1;
break;
}
break;

case 2: if ((x = s[0] &~ m[0]) != 0)
sig = 1;
else if ((x = s[1] &~ m[1]) != 0)
sig = _NSIG_BPW + 1;
else
case 2:
x = s[1] &~ m[1];
if (!x)
break;
sig += ffz(~x);
sig = ffz(~x) + _NSIG_BPW + 1;
break;

case 1: if ((x = *s &~ *m) != 0)
sig = ffz(~x) + 1;
case 1:
/* Nothing to do */
break;
}

Expand Down

0 comments on commit a27341c

Please sign in to comment.