Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -317,14 +317,14 @@ ret_from_sys_call:
	ldq	$0, SP_OFF($sp)
	and	$0, 8, $0
	beq	$0, restore_all
ret_from_reschedule:
ret_to_user:
	/* Make sure need_resched and sigpending don't change between
		sampling and the rti.  */
	lda	$16, 7
	call_pal PAL_swpipl
	ldl	$5, TI_FLAGS($8)
	and	$5, _TIF_WORK_MASK, $2
	bne	$5, work_pending
	bne	$2, work_pending
restore_all:
	RESTORE_ALL
	call_pal PAL_rti
@@ -363,7 +363,7 @@ $ret_success:
 *       $8: current.
 *      $19: The old syscall number, or zero if this is not a return
 *           from a syscall that errored and is possibly restartable.
 *      $20: Error indication.
 *      $20: The old a3 value
 */

	.align	4
@@ -392,12 +392,18 @@ $work_resched:

$work_notifysig:
	mov	$sp, $16
	br	$1, do_switch_stack
	bsr	$1, do_switch_stack
	mov	$sp, $17
	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
	mov	$9, $19
	mov	$10, $20
	bsr	$1, undo_switch_stack
	br	restore_all
	br	ret_to_user
.end work_pending

/*