]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
[S390] signal race with restarting system calls
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 26 Sep 2011 14:42:54 +0000 (16:42 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 26 Sep 2011 14:42:47 +0000 (16:42 +0200)
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>
arch/s390/include/asm/ptrace.h
arch/s390/include/asm/syscall.h
arch/s390/include/asm/thread_info.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/compat_signal.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kernel/ptrace.c
arch/s390/kernel/signal.c
include/linux/elf.h

index 62fd80c9e98cbb6b2944cba67398ee746870d1c3..93c907b4776fa08a8bc55aa629bef064cbae3f4b 100644 (file)
@@ -328,8 +328,7 @@ struct pt_regs
        psw_t psw;
        unsigned long gprs[NUM_GPRS];
        unsigned long orig_gpr2;
-       unsigned short ilc;
-       unsigned short svcnr;
+       unsigned int svc_code;
 };
 
 /*
@@ -487,6 +486,8 @@ typedef struct
 #define PTRACE_POKETEXT_AREA         0x5004
 #define PTRACE_POKEDATA_AREA         0x5005
 #define PTRACE_GET_LAST_BREAK        0x5006
+#define PTRACE_PEEK_SYSTEM_CALL       0x5007
+#define PTRACE_POKE_SYSTEM_CALL              0x5008
 
 /*
  * PT_PROT definition is loosely based on hppa bsd definition in
index 5c0246b955d80e411250623c400c3387f23568b7..614267f6071353cc9527194fe251923b51b6fba9 100644 (file)
@@ -13,6 +13,7 @@
 #define _ASM_SYSCALL_H 1
 
 #include <linux/sched.h>
+#include <linux/err.h>
 #include <asm/ptrace.h>
 
 /*
@@ -25,7 +26,7 @@ extern const unsigned int sys_call_table[];
 static inline long syscall_get_nr(struct task_struct *task,
                                  struct pt_regs *regs)
 {
-       return regs->svcnr ? regs->svcnr : -1;
+       return regs->svc_code ? (regs->svc_code & 0xffff) : -1;
 }
 
 static inline void syscall_rollback(struct task_struct *task,
@@ -37,7 +38,7 @@ static inline void syscall_rollback(struct task_struct *task,
 static inline long syscall_get_error(struct task_struct *task,
                                     struct pt_regs *regs)
 {
-       return (regs->gprs[2] >= -4096UL) ? -regs->gprs[2] : 0;
+       return IS_ERR_VALUE(regs->gprs[2]) ? regs->gprs[2] : 0;
 }
 
 static inline long syscall_get_return_value(struct task_struct *task,
index f9a9a10979c9bfe59838fd9904f647d529d4da1a..8655da6dda745e600988bed45ed1a2db0aa46086 100644 (file)
@@ -48,6 +48,7 @@ struct thread_info {
        unsigned int            cpu;            /* current CPU */
        int                     preempt_count;  /* 0 => preemptable, <0 => BUG */
        struct restart_block    restart_block;
+       unsigned long           system_call;
        __u64                   user_timer;
        __u64                   system_timer;
        unsigned long           last_break;     /* last breaking-event-address. */
index 1140035b1cd8c68e00531c8dfc99292803ab1cb9..751318765e2e95ba1a2d5c56e683e13de0eb9925 100644 (file)
@@ -45,8 +45,7 @@ int main(void)
        DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
        DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
        DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
-       DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc));
-       DEFINE(__PT_SVCNR, offsetof(struct pt_regs, svcnr));
+       DEFINE(__PT_SVC_CODE, offsetof(struct pt_regs, svc_code));
        DEFINE(__PT_SIZE, sizeof(struct pt_regs));
        BLANK();
        DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
index a9a285b8c4ad3ccdfafcaf2b1412de4900e2b219..d7c8e54c32e7f285f874bcb50a9ae074cb4c7cb0 100644 (file)
@@ -342,7 +342,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
                return err;
 
        restore_fp_regs(&current->thread.fp_regs);
-       regs->svcnr = 0;        /* disable syscall checks */
+       regs->svc_code = 0;     /* disable syscall checks */
        return 0;
 }
 
index 195387ab7c983ca42496c2f8ca927a4176a05b13..7ec87a7c5a96a3908374657e43b53ef8bc6216c8 100644 (file)
@@ -43,8 +43,7 @@ SP_R13             =  STACK_FRAME_OVERHEAD + __PT_GPRS + 52
 SP_R14      =  STACK_FRAME_OVERHEAD + __PT_GPRS + 56
 SP_R15      =  STACK_FRAME_OVERHEAD + __PT_GPRS + 60
 SP_ORIG_R2   = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
-SP_ILC      =  STACK_FRAME_OVERHEAD + __PT_ILC
-SP_SVCNR     = STACK_FRAME_OVERHEAD + __PT_SVCNR
+SP_SVC_CODE  = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
 SP_SIZE      = STACK_FRAME_OVERHEAD + __PT_SIZE
 
 _TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
@@ -229,7 +228,7 @@ sysc_saveall:
        SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(8,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        l       %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
 sysc_vtime:
        UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
@@ -239,12 +238,12 @@ sysc_update:
        mvc     __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
 sysc_do_svc:
        xr      %r7,%r7
-       icm     %r7,3,SP_SVCNR(%r15)    # load svc number and test for svc 0
+       icm     %r7,3,SP_SVC_CODE+2(%r15)# load svc number and test for svc 0
        bnz     BASED(sysc_nr_ok)       # svc number > 0
        # svc 0: system call number in %r1
        cl      %r1,BASED(.Lnr_syscalls)
        bnl     BASED(sysc_nr_ok)
-       sth     %r1,SP_SVCNR(%r15)
+       sth     %r1,SP_SVC_CODE+2(%r15)
        lr      %r7,%r1           # copy svc number to %r7
 sysc_nr_ok:
        sll     %r7,2             # svc number *4
@@ -335,10 +334,9 @@ sysc_notify_resume:
 #
 sysc_restart:
        ni      __TI_flags+3(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
-       l       %r7,SP_R2(%r15)         # load new svc number
-       mvc     SP_R2(4,%r15),SP_ORIG_R2(%r15) # restore first argument
+       xr      %r7,%r7
+       icm     %r7,3,SP_SVC_CODE+2(%r15)# load new svc number
        lm      %r2,%r6,SP_R2(%r15)     # load svc arguments
-       sth     %r7,SP_SVCNR(%r15)
        b       BASED(sysc_nr_ok)       # restart svc
 
 #
@@ -346,7 +344,7 @@ sysc_restart:
 #
 sysc_singlestep:
        ni      __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
-       xc      SP_SVCNR(2,%r15),SP_SVCNR(%r15)         # clear svc number
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)   # clear svc code
        la      %r2,SP_PTREGS(%r15)     # address of register-save area
        l       %r1,BASED(.Lhandle_per) # load adr. of per handler
        la      %r14,BASED(sysc_return) # load adr. of system return
@@ -361,7 +359,7 @@ sysc_tracesys:
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
        la      %r3,0
        xr      %r0,%r0
-       icm     %r0,3,SP_SVCNR(%r15)
+       icm     %r0,3,SP_SVC_CODE(%r15)
        st      %r0,SP_R2(%r15)
        basr    %r14,%r1
        cl      %r2,BASED(.Lnr_syscalls)
@@ -454,7 +452,7 @@ ENTRY(pgm_check_handler)
        bnz     BASED(pgm_per)          # got per exception -> special case
        SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
-       xc      SP_ILC(4,%r15),SP_ILC(%r15)
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
        mvc     SP_PSW(8,%r15),__LC_PGM_OLD_PSW
        l       %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
        tm      SP_PSW+1(%r15),0x01     # interrupting from user ?
@@ -531,7 +529,7 @@ pgm_svcper:
        SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(8,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        l       %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
        UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
        UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
@@ -550,7 +548,7 @@ pgm_svcper:
 #
 kernel_per:
        REENABLE_IRQS
-       xc      SP_SVCNR(2,%r15),SP_SVCNR(%r15)
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
        la      %r2,SP_PTREGS(%r15)     # address of register-save area
        l       %r1,BASED(.Lhandle_per) # load adr. of per handler
        basr    %r14,%r1                # branch to do_single_step
@@ -966,7 +964,7 @@ cleanup_system_call:
        st      %r15,12(%r12)
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(8,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        mvc     0(4,%r12),__LC_THREAD_INFO
 cleanup_vtime:
        clc     __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12)
index 3257f7f555515f99ed94d74b684486dd2ab16a79..8ecee2019d162435e7e6b563c9c4155611d4d8cc 100644 (file)
@@ -43,8 +43,7 @@ SP_R13             =  STACK_FRAME_OVERHEAD + __PT_GPRS + 104
 SP_R14      =  STACK_FRAME_OVERHEAD + __PT_GPRS + 112
 SP_R15      =  STACK_FRAME_OVERHEAD + __PT_GPRS + 120
 SP_ORIG_R2   = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
-SP_ILC      =  STACK_FRAME_OVERHEAD + __PT_ILC
-SP_SVCNR      =        STACK_FRAME_OVERHEAD + __PT_SVCNR
+SP_SVC_CODE  = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
 SP_SIZE      = STACK_FRAME_OVERHEAD + __PT_SIZE
 
 STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
@@ -250,7 +249,7 @@ sysc_saveall:
        SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(16,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        lg      %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
 sysc_vtime:
        UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
@@ -260,14 +259,14 @@ sysc_update:
        mvc     __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
        LAST_BREAK
 sysc_do_svc:
-       llgh    %r7,SP_SVCNR(%r15)
+       llgh    %r7,SP_SVC_CODE+2(%r15)
        slag    %r7,%r7,2       # shift and test for svc 0
        jnz     sysc_nr_ok
        # svc 0: system call number in %r1
        llgfr   %r1,%r1         # clear high word in r1
        cghi    %r1,NR_syscalls
        jnl     sysc_nr_ok
-       sth     %r1,SP_SVCNR(%r15)
+       sth     %r1,SP_SVC_CODE+2(%r15)
        slag    %r7,%r1,2       # shift and test for svc 0
 sysc_nr_ok:
        larl    %r10,sys_call_table
@@ -358,10 +357,8 @@ sysc_notify_resume:
 #
 sysc_restart:
        ni      __TI_flags+7(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC
-       lg      %r7,SP_R2(%r15)         # load new svc number
-       mvc     SP_R2(8,%r15),SP_ORIG_R2(%r15) # restore first argument
+       lh      %r7,SP_SVC_CODE+2(%r15) # load new svc number
        lmg     %r2,%r6,SP_R2(%r15)     # load svc arguments
-       sth     %r7,SP_SVCNR(%r15)
        slag    %r7,%r7,2
        j       sysc_nr_ok              # restart svc
 
@@ -370,7 +367,7 @@ sysc_restart:
 #
 sysc_singlestep:
        ni      __TI_flags+7(%r12),255-_TIF_PER_TRAP    # clear TIF_PER_TRAP
-       xc      SP_SVCNR(2,%r15),SP_SVCNR(%r15)         # clear svc number
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)   # clear svc code
        la      %r2,SP_PTREGS(%r15)     # address of register-save area
        larl    %r14,sysc_return        # load adr. of system return
        jg      do_per_trap
@@ -382,7 +379,7 @@ sysc_singlestep:
 sysc_tracesys:
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
        la      %r3,0
-       llgh    %r0,SP_SVCNR(%r15)
+       llgh    %r0,SP_SVC_CODE+2(%r15)
        stg     %r0,SP_R2(%r15)
        brasl   %r14,do_syscall_trace_enter
        lghi    %r0,NR_syscalls
@@ -470,7 +467,7 @@ ENTRY(pgm_check_handler)
        jnz     pgm_per                  # got per exception -> special case
        SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
-       xc      SP_ILC(4,%r15),SP_ILC(%r15)
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)
        mvc     SP_PSW(16,%r15),__LC_PGM_OLD_PSW
        lg      %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
        HANDLE_SIE_INTERCEPT
@@ -551,7 +548,7 @@ pgm_svcper:
        SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(16,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        lg      %r12,__LC_THREAD_INFO   # load pointer to thread_info struct
        UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
        UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
@@ -571,7 +568,7 @@ pgm_svcper:
 #
 kernel_per:
        REENABLE_IRQS
-       xc      SP_SVCNR(2,%r15),SP_SVCNR(%r15) # clear svc number
+       xc      SP_SVC_CODE(4,%r15),SP_SVC_CODE(%r15)   # clear svc number
        la      %r2,SP_PTREGS(%r15)     # address of register-save area
        brasl   %r14,do_per_trap
        j       pgm_exit
@@ -973,7 +970,7 @@ cleanup_system_call:
        stg     %r11,0(%r12)
        CREATE_STACK_FRAME __LC_SAVE_AREA
        mvc     SP_PSW(16,%r15),__LC_SVC_OLD_PSW
-       mvc     SP_ILC(4,%r15),__LC_SVC_ILC
+       mvc     SP_SVC_CODE(4,%r15),__LC_SVC_ILC
        mvc     8(8,%r12),__LC_THREAD_INFO
 cleanup_vtime:
        clc     __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24)
index ef86ad2439868ffd4889b56f378f19e5c1f11968..8216fd70eaaa6473d43d88a65c208811637aadaf 100644 (file)
@@ -42,6 +42,7 @@ enum s390_regset {
        REGSET_GENERAL,
        REGSET_FP,
        REGSET_LAST_BREAK,
+       REGSET_SYSTEM_CALL,
        REGSET_GENERAL_EXTENDED,
 };
 
@@ -301,6 +302,13 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
                           high order bit but older gdb's rely on it */
                        data |= PSW_ADDR_AMODE;
 #endif
+               if (addr == (addr_t) &dummy->regs.psw.addr)
+                       /*
+                        * The debugger changed the instruction address,
+                        * reset system call restart, see signal.c:do_signal
+                        */
+                       current_thread_info()->system_call = 0;
+
                *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
 
        } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
@@ -608,6 +616,11 @@ static int __poke_user_compat(struct task_struct *child,
                        /* Build a 64 bit psw address from 31 bit address. */
                        task_pt_regs(child)->psw.addr =
                                (__u64) tmp & PSW32_ADDR_INSN;
+                       /*
+                        * The debugger changed the instruction address,
+                        * reset system call restart, see signal.c:do_signal
+                        */
+                       current_thread_info()->system_call = 0;
                } else {
                        /* gpr 0-15 */
                        *(__u32*)((addr_t) &task_pt_regs(child)->psw
@@ -735,7 +748,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
                 * debugger stored an invalid system call number. Skip
                 * the system call and the system call restart handling.
                 */
-               regs->svcnr = 0;
+               regs->svc_code = 0;
                ret = -1;
        }
 
@@ -897,6 +910,46 @@ static int s390_last_break_get(struct task_struct *target,
 
 #endif
 
+static int s390_system_call_get(struct task_struct *target,
+                               const struct user_regset *regset,
+                               unsigned int pos, unsigned int count,
+                               void *kbuf, void __user *ubuf)
+{
+       unsigned long system_call;
+
+       system_call = task_thread_info(target)->system_call;
+       if (kbuf) {
+               unsigned long *k = kbuf;
+               *k = system_call;
+       } else {
+               unsigned long __user *u = ubuf;
+               if (__put_user(system_call, u))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+static int s390_system_call_set(struct task_struct *target,
+                               const struct user_regset *regset,
+                               unsigned int pos, unsigned int count,
+                               const void *kbuf, const void __user *ubuf)
+{
+       unsigned long system_call;
+       int rc;
+
+       if (kbuf) {
+               const unsigned long *k = kbuf;
+               system_call = *k;
+       } else {
+               const unsigned long __user *u = ubuf;
+               rc = __get_user(system_call, u);
+               if (rc)
+                       return rc;
+       }
+       task_thread_info(target)->system_call = system_call;
+       return 0;
+}
+
 static const struct user_regset s390_regsets[] = {
        [REGSET_GENERAL] = {
                .core_note_type = NT_PRSTATUS,
@@ -923,6 +976,14 @@ static const struct user_regset s390_regsets[] = {
                .get = s390_last_break_get,
        },
 #endif
+       [REGSET_SYSTEM_CALL] = {
+               .core_note_type = NT_S390_SYSTEM_CALL,
+               .n = 1,
+               .size = sizeof(long),
+               .align = sizeof(long),
+               .get = s390_system_call_get,
+               .set = s390_system_call_set,
+       },
 };
 
 static const struct user_regset_view user_s390_view = {
@@ -1078,6 +1139,47 @@ static int s390_compat_last_break_get(struct task_struct *target,
        return 0;
 }
 
+static int s390_compat_system_call_get(struct task_struct *target,
+                                      const struct user_regset *regset,
+                                      unsigned int pos, unsigned int count,
+                                      void *kbuf, void __user *ubuf)
+{
+       compat_ulong_t system_call;
+
+       system_call = (compat_ulong_t) task_thread_info(target)->system_call;
+       if (kbuf) {
+               compat_ulong_t *k = kbuf;
+               *k = system_call;
+       } else {
+               compat_ulong_t __user *u = ubuf;
+               if (__put_user(system_call, u))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+static int s390_compat_system_call_set(struct task_struct *target,
+                                      const struct user_regset *regset,
+                                      unsigned int pos, unsigned int count,
+                                      const void *kbuf,
+                                      const void __user *ubuf)
+{
+       compat_ulong_t system_call;
+       int rc;
+
+       if (kbuf) {
+               const compat_ulong_t *k = kbuf;
+               system_call = *k;
+       } else {
+               const compat_ulong_t  __user *u = ubuf;
+               rc = __get_user(system_call, u);
+               if (rc)
+                       return rc;
+       }
+       task_thread_info(target)->system_call = (unsigned long) system_call;
+       return 0;
+}
+
 static const struct user_regset s390_compat_regsets[] = {
        [REGSET_GENERAL] = {
                .core_note_type = NT_PRSTATUS,
@@ -1102,6 +1204,14 @@ static const struct user_regset s390_compat_regsets[] = {
                .align = sizeof(long),
                .get = s390_compat_last_break_get,
        },
+       [REGSET_SYSTEM_CALL] = {
+               .core_note_type = NT_S390_SYSTEM_CALL,
+               .n = 1,
+               .size = sizeof(long),
+               .align = sizeof(long),
+               .get = s390_compat_system_call_get,
+               .set = s390_compat_system_call_set,
+       },
        [REGSET_GENERAL_EXTENDED] = {
                .core_note_type = NT_S390_HIGH_GPRS,
                .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
index 9a40e1cc5ec3f5e2b5ce15268b35862d22aff28e..e751cab80e04b881f52697f645de3016ff8d5687 100644 (file)
@@ -30,6 +30,7 @@
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/lowcore.h>
+#include <asm/compat.h>
 #include "entry.h"
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
@@ -156,7 +157,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
        current->thread.fp_regs.fpc &= FPC_VALID_MASK;
 
        restore_fp_regs(&current->thread.fp_regs);
-       regs->svcnr = 0;        /* disable syscall checks */
+       regs->svc_code = 0;     /* disable syscall checks */
        return 0;
 }
 
@@ -401,7 +402,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
  */
 void do_signal(struct pt_regs *regs)
 {
-       unsigned long retval = 0, continue_addr = 0, restart_addr = 0;
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
@@ -421,54 +421,43 @@ void do_signal(struct pt_regs *regs)
        else
                oldset = &current->blocked;
 
-       /* Are we from a system call? */
-       if (regs->svcnr) {
-               continue_addr = regs->psw.addr;
-               restart_addr = continue_addr - regs->ilc;
-               retval = regs->gprs[2];
-
-               /* Prepare for system call restart.  We do this here so that a
-                  debugger will see the already changed PSW. */
-               switch (retval) {
-               case -ERESTARTNOHAND:
-               case -ERESTARTSYS:
-               case -ERESTARTNOINTR:
-                       regs->gprs[2] = regs->orig_gpr2;
-                       regs->psw.addr = restart_addr;
-                       break;
-               case -ERESTART_RESTARTBLOCK:
-                       regs->gprs[2] = -EINTR;
-               }
-               regs->svcnr = 0;        /* Don't deal with this again. */
-       }
-
-       /* Get signal to deliver.  When running under ptrace, at this point
-          the debugger may change all our registers ... */
+       /*
+        * Get signal to deliver. When running under ptrace, at this point
+        * the debugger may change all our registers, including the system
+        * call information.
+        */
+       current_thread_info()->system_call = regs->svc_code;
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
-
-       /* Depending on the signal settings we may need to revert the
-          decision to restart the system call. */
-       if (signr > 0 && regs->psw.addr == restart_addr) {
-               if (retval == -ERESTARTNOHAND
-                   || (retval == -ERESTARTSYS
-                        && !(current->sighand->action[signr-1].sa.sa_flags
-                             & SA_RESTART))) {
-                       regs->gprs[2] = -EINTR;
-                       regs->psw.addr = continue_addr;
-               }
-       }
+       regs->svc_code = current_thread_info()->system_call;
 
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
-               int ret;
-#ifdef CONFIG_COMPAT
-               if (is_compat_task()) {
-                       ret = handle_signal32(signr, &ka, &info, oldset, regs);
-               }
-               else
-#endif
-                       ret = handle_signal(signr, &ka, &info, oldset, regs);
-               if (!ret) {
+               if (regs->svc_code > 0) {
+                       /* Check for system call restarting. */
+                       switch (regs->gprs[2]) {
+                       case -ERESTART_RESTARTBLOCK:
+                       case -ERESTARTNOHAND:
+                               regs->gprs[2] = -EINTR;
+                               break;
+                       case -ERESTARTSYS:
+                               if (!(ka.sa.sa_flags & SA_RESTART)) {
+                                       regs->gprs[2] = -EINTR;
+                                       break;
+                               }
+                       /* fallthrough */
+                       case -ERESTARTNOINTR:
+                               regs->gprs[2] = regs->orig_gpr2;
+                               regs->psw.addr = regs->psw.addr -
+                                       (regs->svc_code >> 16);
+                               break;
+                       }
+                       /* No longer in a system call */
+                       regs->svc_code = 0;
+               }
+
+               if ((is_compat_task() ?
+                    handle_signal32(signr, &ka, &info, oldset, regs) :
+                    handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
                        /*
                         * A signal was successfully delivered; the saved
                         * sigmask will have been stored in the signal frame,
@@ -482,11 +471,28 @@ void do_signal(struct pt_regs *regs)
                         * Let tracing know that we've done the handler setup.
                         */
                        tracehook_signal_handler(signr, &info, &ka, regs,
-                                       test_thread_flag(TIF_SINGLE_STEP));
+                                        test_thread_flag(TIF_SINGLE_STEP));
                }
                return;
        }
 
+       /* No handlers present - check for system call restart */
+       if (regs->svc_code > 0) {
+               switch (regs->gprs[2]) {
+               case -ERESTART_RESTARTBLOCK:
+                       /* Restart with sys_restart_syscall */
+                       regs->svc_code = __NR_restart_syscall;
+               /* fallthrough */
+               case -ERESTARTNOHAND:
+               case -ERESTARTSYS:
+               case -ERESTARTNOINTR:
+                       /* Restart system call with magic TIF bit. */
+                       regs->gprs[2] = regs->orig_gpr2;
+                       set_thread_flag(TIF_RESTART_SVC);
+                       break;
+               }
+       }
+
        /*
         * If there's no signal to deliver, we just put the saved sigmask back.
         */
@@ -494,13 +500,6 @@ void do_signal(struct pt_regs *regs)
                clear_thread_flag(TIF_RESTORE_SIGMASK);
                sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
        }
-
-       /* Restart a different system call. */
-       if (retval == -ERESTART_RESTARTBLOCK
-           && regs->psw.addr == continue_addr) {
-               regs->gprs[2] = __NR_restart_syscall;
-               set_thread_flag(TIF_RESTART_SVC);
-       }
 }
 
 void do_notify_resume(struct pt_regs *regs)
index 110821cb6ea5fb5c8789da31f26af10ee61fc68d..31f0508d7da7a3274e207cc3389edb824ed77fd3 100644 (file)
@@ -395,6 +395,7 @@ typedef struct elf64_shdr {
 #define NT_S390_CTRS   0x304           /* s390 control registers */
 #define NT_S390_PREFIX 0x305           /* s390 prefix register */
 #define NT_S390_LAST_BREAK     0x306   /* s390 breaking event address */
+#define NT_S390_SYSTEM_CALL    0x307   /* s390 system call restart data */
 #define NT_ARM_VFP     0x400           /* ARM VFP/NEON registers */