aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/signal.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 10:16:47 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-10-30 10:16:43 -0400
commit20b40a794baf3b4b0320c0a77ce944d5d1a01f25 (patch)
treefb5eb62f8f75d8f6a31aae4c3cff3371f41cdd6d /arch/s390/kernel/signal.c
parent3ee49c8f123257c72b74f398ef99ac3348c493cc (diff)
[S390] signal race with restarting system calls
For a ERESTARTNOHAND/ERESTARTSYS/ERESTARTNOINTR restarting system call do_signal will prepare the restart of the system call with a rewind of the PSW before calling get_signal_to_deliver (where the debugger might take control). For A ERESTART_RESTARTBLOCK restarting system call do_signal will set -EINTR as return code. There are two issues with this approach: 1) strace never sees ERESTARTNOHAND, ERESTARTSYS, ERESTARTNOINTR or ERESTART_RESTARTBLOCK as the rewinding already took place or the return code has been changed to -EINTR 2) if get_signal_to_deliver does not return with a signal to deliver the restart via the repeat of the svc instruction is left in place. This opens a race if another signal is made pending before the system call instruction can be reexecuted. The original system call will be restarted even if the second signal would have ended the system call with -EINTR. These two issues can be solved by dropping the early rewind of the system call before get_signal_to_deliver has been called and by using the TIF_RESTART_SVC magic to do the restart if no signal has to be delivered. The only situation where the system call restart via the repeat of the svc instruction is appropriate is when a SA_RESTART signal is delivered to user space. Unfortunately this breaks inferior calls by the debugger again. The system call number and the length of the system call instruction is lost over the inferior call and user space will see ERESTARTNOHAND/ ERESTARTSYS/ERESTARTNOINTR/ERESTART_RESTARTBLOCK. To correct this a new ptrace interface is added to save/restore the system call number and system call instruction length. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/signal.c')
-rw-r--r--arch/s390/kernel/signal.c107
1 files changed, 53 insertions, 54 deletions
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index 9a40e1cc5ec3..e751cab80e04 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -30,6 +30,7 @@
30#include <asm/ucontext.h> 30#include <asm/ucontext.h>
31#include <asm/uaccess.h> 31#include <asm/uaccess.h>
32#include <asm/lowcore.h> 32#include <asm/lowcore.h>
33#include <asm/compat.h>
33#include "entry.h" 34#include "entry.h"
34 35
35#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) 36#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
@@ -156,7 +157,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
156 current->thread.fp_regs.fpc &= FPC_VALID_MASK; 157 current->thread.fp_regs.fpc &= FPC_VALID_MASK;
157 158
158 restore_fp_regs(&current->thread.fp_regs); 159 restore_fp_regs(&current->thread.fp_regs);
159 regs->svcnr = 0; /* disable syscall checks */ 160 regs->svc_code = 0; /* disable syscall checks */
160 return 0; 161 return 0;
161} 162}
162 163
@@ -401,7 +402,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
401 */ 402 */
402void do_signal(struct pt_regs *regs) 403void do_signal(struct pt_regs *regs)
403{ 404{
404 unsigned long retval = 0, continue_addr = 0, restart_addr = 0;
405 siginfo_t info; 405 siginfo_t info;
406 int signr; 406 int signr;
407 struct k_sigaction ka; 407 struct k_sigaction ka;
@@ -421,54 +421,43 @@ void do_signal(struct pt_regs *regs)
421 else 421 else
422 oldset = &current->blocked; 422 oldset = &current->blocked;
423 423
424 /* Are we from a system call? */ 424 /*
425 if (regs->svcnr) { 425 * Get signal to deliver. When running under ptrace, at this point
426 continue_addr = regs->psw.addr; 426 * the debugger may change all our registers, including the system
427 restart_addr = continue_addr - regs->ilc; 427 * call information.
428 retval = regs->gprs[2]; 428 */
429 429 current_thread_info()->system_call = regs->svc_code;
430 /* Prepare for system call restart. We do this here so that a
431 debugger will see the already changed PSW. */
432 switch (retval) {
433 case -ERESTARTNOHAND:
434 case -ERESTARTSYS:
435 case -ERESTARTNOINTR:
436 regs->gprs[2] = regs->orig_gpr2;
437 regs->psw.addr = restart_addr;
438 break;
439 case -ERESTART_RESTARTBLOCK:
440 regs->gprs[2] = -EINTR;
441 }
442 regs->svcnr = 0; /* Don't deal with this again. */
443 }
444
445 /* Get signal to deliver. When running under ptrace, at this point
446 the debugger may change all our registers ... */
447 signr = get_signal_to_deliver(&info, &ka, regs, NULL); 430 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
448 431 regs->svc_code = current_thread_info()->system_call;
449 /* Depending on the signal settings we may need to revert the
450 decision to restart the system call. */
451 if (signr > 0 && regs->psw.addr == restart_addr) {
452 if (retval == -ERESTARTNOHAND
453 || (retval == -ERESTARTSYS
454 && !(current->sighand->action[signr-1].sa.sa_flags
455 & SA_RESTART))) {
456 regs->gprs[2] = -EINTR;
457 regs->psw.addr = continue_addr;
458 }
459 }
460 432
461 if (signr > 0) { 433 if (signr > 0) {
462 /* Whee! Actually deliver the signal. */ 434 /* Whee! Actually deliver the signal. */
463 int ret; 435 if (regs->svc_code > 0) {
464#ifdef CONFIG_COMPAT 436 /* Check for system call restarting. */
465 if (is_compat_task()) { 437 switch (regs->gprs[2]) {
466 ret = handle_signal32(signr, &ka, &info, oldset, regs); 438 case -ERESTART_RESTARTBLOCK:
467 } 439 case -ERESTARTNOHAND:
468 else 440 regs->gprs[2] = -EINTR;
469#endif 441 break;
470 ret = handle_signal(signr, &ka, &info, oldset, regs); 442 case -ERESTARTSYS:
471 if (!ret) { 443 if (!(ka.sa.sa_flags & SA_RESTART)) {
444 regs->gprs[2] = -EINTR;
445 break;
446 }
447 /* fallthrough */
448 case -ERESTARTNOINTR:
449 regs->gprs[2] = regs->orig_gpr2;
450 regs->psw.addr = regs->psw.addr -
451 (regs->svc_code >> 16);
452 break;
453 }
454 /* No longer in a system call */
455 regs->svc_code = 0;
456 }
457
458 if ((is_compat_task() ?
459 handle_signal32(signr, &ka, &info, oldset, regs) :
460 handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
472 /* 461 /*
473 * A signal was successfully delivered; the saved 462 * A signal was successfully delivered; the saved
474 * sigmask will have been stored in the signal frame, 463 * sigmask will have been stored in the signal frame,
@@ -482,11 +471,28 @@ void do_signal(struct pt_regs *regs)
482 * Let tracing know that we've done the handler setup. 471 * Let tracing know that we've done the handler setup.
483 */ 472 */
484 tracehook_signal_handler(signr, &info, &ka, regs, 473 tracehook_signal_handler(signr, &info, &ka, regs,
485 test_thread_flag(TIF_SINGLE_STEP)); 474 test_thread_flag(TIF_SINGLE_STEP));
486 } 475 }
487 return; 476 return;
488 } 477 }
489 478
479 /* No handlers present - check for system call restart */
480 if (regs->svc_code > 0) {
481 switch (regs->gprs[2]) {
482 case -ERESTART_RESTARTBLOCK:
483 /* Restart with sys_restart_syscall */
484 regs->svc_code = __NR_restart_syscall;
485 /* fallthrough */
486 case -ERESTARTNOHAND:
487 case -ERESTARTSYS:
488 case -ERESTARTNOINTR:
489 /* Restart system call with magic TIF bit. */
490 regs->gprs[2] = regs->orig_gpr2;
491 set_thread_flag(TIF_RESTART_SVC);
492 break;
493 }
494 }
495
490 /* 496 /*
491 * If there's no signal to deliver, we just put the saved sigmask back. 497 * If there's no signal to deliver, we just put the saved sigmask back.
492 */ 498 */
@@ -494,13 +500,6 @@ void do_signal(struct pt_regs *regs)
494 clear_thread_flag(TIF_RESTORE_SIGMASK); 500 clear_thread_flag(TIF_RESTORE_SIGMASK);
495 sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL); 501 sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
496 } 502 }
497
498 /* Restart a different system call. */
499 if (retval == -ERESTART_RESTARTBLOCK
500 && regs->psw.addr == continue_addr) {
501 regs->gprs[2] = __NR_restart_syscall;
502 set_thread_flag(TIF_RESTART_SVC);
503 }
504} 503}
505 504
506void do_notify_resume(struct pt_regs *regs) 505void do_notify_resume(struct pt_regs *regs)