aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/ptrace.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/ptrace.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/ptrace.c')
-rw-r--r--arch/s390/kernel/ptrace.c51
1 files changed, 50 insertions, 1 deletions
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index ae0e14b8880..bae1cc49fe9 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -42,6 +42,7 @@ enum s390_regset {
42 REGSET_GENERAL, 42 REGSET_GENERAL,
43 REGSET_FP, 43 REGSET_FP,
44 REGSET_LAST_BREAK, 44 REGSET_LAST_BREAK,
45 REGSET_SYSTEM_CALL,
45 REGSET_GENERAL_EXTENDED, 46 REGSET_GENERAL_EXTENDED,
46}; 47};
47 48
@@ -303,6 +304,13 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
303 high order bit but older gdb's rely on it */ 304 high order bit but older gdb's rely on it */
304 data |= PSW_ADDR_AMODE; 305 data |= PSW_ADDR_AMODE;
305#endif 306#endif
307 if (addr == (addr_t) &dummy->regs.psw.addr)
308 /*
309 * The debugger changed the instruction address,
310 * reset system call restart, see signal.c:do_signal
311 */
312 task_thread_info(child)->system_call = 0;
313
306 *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data; 314 *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
307 315
308 } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { 316 } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
@@ -610,6 +618,11 @@ static int __poke_user_compat(struct task_struct *child,
610 /* Build a 64 bit psw address from 31 bit address. */ 618 /* Build a 64 bit psw address from 31 bit address. */
611 task_pt_regs(child)->psw.addr = 619 task_pt_regs(child)->psw.addr =
612 (__u64) tmp & PSW32_ADDR_INSN; 620 (__u64) tmp & PSW32_ADDR_INSN;
621 /*
622 * The debugger changed the instruction address,
623 * reset system call restart, see signal.c:do_signal
624 */
625 task_thread_info(child)->system_call = 0;
613 } else { 626 } else {
614 /* gpr 0-15 */ 627 /* gpr 0-15 */
615 *(__u32*)((addr_t) &task_pt_regs(child)->psw 628 *(__u32*)((addr_t) &task_pt_regs(child)->psw
@@ -737,7 +750,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
737 * debugger stored an invalid system call number. Skip 750 * debugger stored an invalid system call number. Skip
738 * the system call and the system call restart handling. 751 * the system call and the system call restart handling.
739 */ 752 */
740 regs->svcnr = 0; 753 regs->svc_code = 0;
741 ret = -1; 754 ret = -1;
742 } 755 }
743 756
@@ -899,6 +912,26 @@ static int s390_last_break_get(struct task_struct *target,
899 912
900#endif 913#endif
901 914
915static int s390_system_call_get(struct task_struct *target,
916 const struct user_regset *regset,
917 unsigned int pos, unsigned int count,
918 void *kbuf, void __user *ubuf)
919{
920 unsigned int *data = &task_thread_info(target)->system_call;
921 return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
922 data, 0, sizeof(unsigned int));
923}
924
925static int s390_system_call_set(struct task_struct *target,
926 const struct user_regset *regset,
927 unsigned int pos, unsigned int count,
928 const void *kbuf, const void __user *ubuf)
929{
930 unsigned int *data = &task_thread_info(target)->system_call;
931 return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
932 data, 0, sizeof(unsigned int));
933}
934
902static const struct user_regset s390_regsets[] = { 935static const struct user_regset s390_regsets[] = {
903 [REGSET_GENERAL] = { 936 [REGSET_GENERAL] = {
904 .core_note_type = NT_PRSTATUS, 937 .core_note_type = NT_PRSTATUS,
@@ -925,6 +958,14 @@ static const struct user_regset s390_regsets[] = {
925 .get = s390_last_break_get, 958 .get = s390_last_break_get,
926 }, 959 },
927#endif 960#endif
961 [REGSET_SYSTEM_CALL] = {
962 .core_note_type = NT_S390_SYSTEM_CALL,
963 .n = 1,
964 .size = sizeof(unsigned int),
965 .align = sizeof(unsigned int),
966 .get = s390_system_call_get,
967 .set = s390_system_call_set,
968 },
928}; 969};
929 970
930static const struct user_regset_view user_s390_view = { 971static const struct user_regset_view user_s390_view = {
@@ -1104,6 +1145,14 @@ static const struct user_regset s390_compat_regsets[] = {
1104 .align = sizeof(long), 1145 .align = sizeof(long),
1105 .get = s390_compat_last_break_get, 1146 .get = s390_compat_last_break_get,
1106 }, 1147 },
1148 [REGSET_SYSTEM_CALL] = {
1149 .core_note_type = NT_S390_SYSTEM_CALL,
1150 .n = 1,
1151 .size = sizeof(compat_uint_t),
1152 .align = sizeof(compat_uint_t),
1153 .get = s390_system_call_get,
1154 .set = s390_system_call_set,
1155 },
1107 [REGSET_GENERAL_EXTENDED] = { 1156 [REGSET_GENERAL_EXTENDED] = {
1108 .core_note_type = NT_S390_HIGH_GPRS, 1157 .core_note_type = NT_S390_HIGH_GPRS,
1109 .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), 1158 .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),