Commit 494486a1d2697f2153199b6501ab5b4d6e15a2bb

Authored by Al Viro
Committed by Matt Turner
1 parent 5329363861

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: Michael Cree <mcree@orcon.net.nz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Matt Turner <mattst88@gmail.com>

Showing 1 changed file with 11 additions and 5 deletions Side-by-side Diff

arch/alpha/kernel/entry.S
... ... @@ -317,14 +317,14 @@
317 317 ldq $0, SP_OFF($sp)
318 318 and $0, 8, $0
319 319 beq $0, restore_all
320   -ret_from_reschedule:
  320 +ret_to_user:
321 321 /* Make sure need_resched and sigpending don't change between
322 322 sampling and the rti. */
323 323 lda $16, 7
324 324 call_pal PAL_swpipl
325 325 ldl $5, TI_FLAGS($8)
326 326 and $5, _TIF_WORK_MASK, $2
327   - bne $5, work_pending
  327 + bne $2, work_pending
328 328 restore_all:
329 329 RESTORE_ALL
330 330 call_pal PAL_rti
... ... @@ -363,7 +363,7 @@
363 363 * $8: current.
364 364 * $19: The old syscall number, or zero if this is not a return
365 365 * from a syscall that errored and is possibly restartable.
366   - * $20: Error indication.
  366 + * $20: The old a3 value
367 367 */
368 368  
369 369 .align 4
370 370  
371 371  
372 372  
... ... @@ -392,12 +392,18 @@
392 392  
393 393 $work_notifysig:
394 394 mov $sp, $16
395   - br $1, do_switch_stack
  395 + bsr $1, do_switch_stack
396 396 mov $sp, $17
397 397 mov $5, $18
  398 + mov $19, $9 /* save old syscall number */
  399 + mov $20, $10 /* save old a3 */
  400 + and $5, _TIF_SIGPENDING, $2
  401 + cmovne $2, 0, $9 /* we don't want double syscall restarts */
398 402 jsr $26, do_notify_resume
  403 + mov $9, $19
  404 + mov $10, $20
399 405 bsr $1, undo_switch_stack
400   - br restore_all
  406 + br ret_to_user
401 407 .end work_pending
402 408  
403 409 /*