Commit 494486a1 authored by Al Viro's avatar Al Viro Committed by Matt Turner

alpha: deal with multiple simultaneously pending signals

Unlike the other targets, alpha sets _one_ sigframe and
buggers off until the next syscall/interrupt, even if
more signals are pending.  It leads to quite a few unpleasant
inconsistencies, starting with SIGSEGV potentially arriving
not where it should and including e.g. mess with sigsuspend();
consider two pending signals blocked until sigsuspend()
unblocks them.  We pick the first one; then, if we are hit
by interrupt while in the handler, we process the second one
as well.  If we are not, and if no syscalls had been made,
we get out of the first handler and leave the second signal
pending; normally sigreturn() would've picked it anyway, but
here it starts with restoring the original mask and voila -
the second signal is blocked again.  On everything else we
get both delivered consistently.

It's actually easy to fix; the only thing to watch out for
is prevention of double syscall restart.  Fortunately, the
idea I've nicked from arm fix by rmk works just fine...

Testcase demonstrating the behaviour in question; on alpha
we get one or both flags set (usually one), on everything
else both are always set.
	#include <signal.h>
	#include <stdio.h>
	int had1, had2;
	void f1(int sig) { had1 = 1; }
	void f2(int sig) { had2 = 1; }
	main()
	{
		sigset_t set1, set2;
		sigemptyset(&set1);
		sigemptyset(&set2);
		sigaddset(&set2, 1);
		sigaddset(&set2, 2);
		signal(1, f1);
		signal(2, f2);
		sigprocmask(SIG_SETMASK, &set2, NULL);
		raise(1);
		raise(2);
		sigsuspend(&set1);
		printf("had1:%d had2:%d\n", had1, had2);
	}
Tested-by: default avatarMichael Cree <mcree@orcon.net.nz>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarMatt Turner <mattst88@gmail.com>
parent 53293638
...@@ -317,14 +317,14 @@ ret_from_sys_call: ...@@ -317,14 +317,14 @@ ret_from_sys_call:
ldq $0, SP_OFF($sp) ldq $0, SP_OFF($sp)
and $0, 8, $0 and $0, 8, $0
beq $0, restore_all beq $0, restore_all
ret_from_reschedule: ret_to_user:
/* Make sure need_resched and sigpending don't change between /* Make sure need_resched and sigpending don't change between
sampling and the rti. */ sampling and the rti. */
lda $16, 7 lda $16, 7
call_pal PAL_swpipl call_pal PAL_swpipl
ldl $5, TI_FLAGS($8) ldl $5, TI_FLAGS($8)
and $5, _TIF_WORK_MASK, $2 and $5, _TIF_WORK_MASK, $2
bne $5, work_pending bne $2, work_pending
restore_all: restore_all:
RESTORE_ALL RESTORE_ALL
call_pal PAL_rti call_pal PAL_rti
...@@ -363,7 +363,7 @@ $ret_success: ...@@ -363,7 +363,7 @@ $ret_success:
* $8: current. * $8: current.
* $19: The old syscall number, or zero if this is not a return * $19: The old syscall number, or zero if this is not a return
* from a syscall that errored and is possibly restartable. * from a syscall that errored and is possibly restartable.
* $20: Error indication. * $20: The old a3 value
*/ */
.align 4 .align 4
...@@ -392,12 +392,18 @@ $work_resched: ...@@ -392,12 +392,18 @@ $work_resched:
$work_notifysig: $work_notifysig:
mov $sp, $16 mov $sp, $16
br $1, do_switch_stack bsr $1, do_switch_stack
mov $sp, $17 mov $sp, $17
mov $5, $18 mov $5, $18
mov $19, $9 /* save old syscall number */
mov $20, $10 /* save old a3 */
and $5, _TIF_SIGPENDING, $2
cmovne $2, 0, $9 /* we don't want double syscall restarts */
jsr $26, do_notify_resume jsr $26, do_notify_resume
mov $9, $19
mov $10, $20
bsr $1, undo_switch_stack bsr $1, undo_switch_stack
br restore_all br ret_to_user
.end work_pending .end work_pending
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment