Commit 20b40a79 authored by Martin Schwidefsky's avatar Martin Schwidefsky

[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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 3ee49c8f
...@@ -328,8 +328,7 @@ struct pt_regs ...@@ -328,8 +328,7 @@ struct pt_regs
psw_t psw; psw_t psw;
unsigned long gprs[NUM_GPRS]; unsigned long gprs[NUM_GPRS];
unsigned long orig_gpr2; unsigned long orig_gpr2;
unsigned short ilc; unsigned int svc_code;
unsigned short svcnr;
}; };
/* /*
...@@ -487,6 +486,8 @@ typedef struct ...@@ -487,6 +486,8 @@ typedef struct
#define PTRACE_POKETEXT_AREA 0x5004 #define PTRACE_POKETEXT_AREA 0x5004
#define PTRACE_POKEDATA_AREA 0x5005 #define PTRACE_POKEDATA_AREA 0x5005
#define PTRACE_GET_LAST_BREAK 0x5006 #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 * PT_PROT definition is loosely based on hppa bsd definition in
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define _ASM_SYSCALL_H 1 #define _ASM_SYSCALL_H 1
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/err.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
/* /*
...@@ -25,7 +26,7 @@ extern const unsigned int sys_call_table[]; ...@@ -25,7 +26,7 @@ extern const unsigned int sys_call_table[];
static inline long syscall_get_nr(struct task_struct *task, static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs) 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, static inline void syscall_rollback(struct task_struct *task,
...@@ -37,7 +38,7 @@ 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, static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs) 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, static inline long syscall_get_return_value(struct task_struct *task,
......
...@@ -48,6 +48,7 @@ struct thread_info { ...@@ -48,6 +48,7 @@ struct thread_info {
unsigned int cpu; /* current CPU */ unsigned int cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */ int preempt_count; /* 0 => preemptable, <0 => BUG */
struct restart_block restart_block; struct restart_block restart_block;
unsigned int system_call;
__u64 user_timer; __u64 user_timer;
__u64 system_timer; __u64 system_timer;
unsigned long last_break; /* last breaking-event-address. */ unsigned long last_break; /* last breaking-event-address. */
......
...@@ -45,8 +45,7 @@ int main(void) ...@@ -45,8 +45,7 @@ int main(void)
DEFINE(__PT_PSW, offsetof(struct pt_regs, psw)); DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs)); DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2)); DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc)); DEFINE(__PT_SVC_CODE, offsetof(struct pt_regs, svc_code));
DEFINE(__PT_SVCNR, offsetof(struct pt_regs, svcnr));
DEFINE(__PT_SIZE, sizeof(struct pt_regs)); DEFINE(__PT_SIZE, sizeof(struct pt_regs));
BLANK(); BLANK();
DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain)); DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
......
...@@ -342,7 +342,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -342,7 +342,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
return err; return err;
restore_fp_regs(&current->thread.fp_regs); restore_fp_regs(&current->thread.fp_regs);
regs->svcnr = 0; /* disable syscall checks */ regs->svc_code = 0; /* disable syscall checks */
return 0; return 0;
} }
......
...@@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 52 ...@@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 52
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56 SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 60 SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 60
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2 SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC SP_SVC_CODE = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
SP_SVCNR = STACK_FRAME_OVERHEAD + __PT_SVCNR
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ _TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
...@@ -229,7 +228,7 @@ sysc_saveall: ...@@ -229,7 +228,7 @@ sysc_saveall:
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW 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 l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
sysc_vtime: sysc_vtime:
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
...@@ -239,12 +238,12 @@ sysc_update: ...@@ -239,12 +238,12 @@ sysc_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
sysc_do_svc: sysc_do_svc:
xr %r7,%r7 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 bnz BASED(sysc_nr_ok) # svc number > 0
# svc 0: system call number in %r1 # svc 0: system call number in %r1
cl %r1,BASED(.Lnr_syscalls) cl %r1,BASED(.Lnr_syscalls)
bnl BASED(sysc_nr_ok) 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 lr %r7,%r1 # copy svc number to %r7
sysc_nr_ok: sysc_nr_ok:
sll %r7,2 # svc number *4 sll %r7,2 # svc number *4
...@@ -335,10 +334,11 @@ sysc_notify_resume: ...@@ -335,10 +334,11 @@ sysc_notify_resume:
# #
sysc_restart: sysc_restart:
ni __TI_flags+3(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC 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
lm %r2,%r6,SP_R2(%r15) # load svc arguments lm %r2,%r6,SP_R2(%r15) # load svc arguments
sth %r7,SP_SVCNR(%r15) xr %r7,%r7 # svc 0 returns -ENOSYS
clc SP_SVC_CODE+2(%r15),BASED(.Lnr_syscalls+2)
bnl BASED(sysc_nr_ok) # invalid svc number -> do svc 0
icm %r7,3,SP_SVC_CODE+2(%r15)# load new svc number
b BASED(sysc_nr_ok) # restart svc b BASED(sysc_nr_ok) # restart svc
# #
...@@ -346,7 +346,7 @@ sysc_restart: ...@@ -346,7 +346,7 @@ sysc_restart:
# #
sysc_singlestep: sysc_singlestep:
ni __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP 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 la %r2,SP_PTREGS(%r15) # address of register-save area
l %r1,BASED(.Lhandle_per) # load adr. of per handler l %r1,BASED(.Lhandle_per) # load adr. of per handler
la %r14,BASED(sysc_return) # load adr. of system return la %r14,BASED(sysc_return) # load adr. of system return
...@@ -361,7 +361,7 @@ sysc_tracesys: ...@@ -361,7 +361,7 @@ sysc_tracesys:
la %r2,SP_PTREGS(%r15) # load pt_regs la %r2,SP_PTREGS(%r15) # load pt_regs
la %r3,0 la %r3,0
xr %r0,%r0 xr %r0,%r0
icm %r0,3,SP_SVCNR(%r15) icm %r0,3,SP_SVC_CODE(%r15)
st %r0,SP_R2(%r15) st %r0,SP_R2(%r15)
basr %r14,%r1 basr %r14,%r1
cl %r2,BASED(.Lnr_syscalls) cl %r2,BASED(.Lnr_syscalls)
...@@ -454,7 +454,7 @@ ENTRY(pgm_check_handler) ...@@ -454,7 +454,7 @@ ENTRY(pgm_check_handler)
bnz BASED(pgm_per) # got per exception -> special case bnz BASED(pgm_per) # got per exception -> special case
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __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 mvc SP_PSW(8,%r15),__LC_PGM_OLD_PSW
l %r12,__LC_THREAD_INFO # load pointer to thread_info struct l %r12,__LC_THREAD_INFO # load pointer to thread_info struct
tm SP_PSW+1(%r15),0x01 # interrupting from user ? tm SP_PSW+1(%r15),0x01 # interrupting from user ?
...@@ -531,7 +531,7 @@ pgm_svcper: ...@@ -531,7 +531,7 @@ pgm_svcper:
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW 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 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_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
...@@ -550,7 +550,7 @@ pgm_svcper: ...@@ -550,7 +550,7 @@ pgm_svcper:
# #
kernel_per: kernel_per:
REENABLE_IRQS 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 la %r2,SP_PTREGS(%r15) # address of register-save area
l %r1,BASED(.Lhandle_per) # load adr. of per handler l %r1,BASED(.Lhandle_per) # load adr. of per handler
basr %r14,%r1 # branch to do_single_step basr %r14,%r1 # branch to do_single_step
...@@ -966,7 +966,7 @@ cleanup_system_call: ...@@ -966,7 +966,7 @@ cleanup_system_call:
st %r15,12(%r12) st %r15,12(%r12)
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(8,%r15),__LC_SVC_OLD_PSW 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 mvc 0(4,%r12),__LC_THREAD_INFO
cleanup_vtime: cleanup_vtime:
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12) clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12)
......
...@@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 104 ...@@ -43,8 +43,7 @@ SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 104
SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 112 SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 112
SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 120 SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 120
SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2 SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2
SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC SP_SVC_CODE = STACK_FRAME_OVERHEAD + __PT_SVC_CODE
SP_SVCNR = STACK_FRAME_OVERHEAD + __PT_SVCNR
SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE
STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
...@@ -250,7 +249,7 @@ sysc_saveall: ...@@ -250,7 +249,7 @@ sysc_saveall:
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW 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 lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
sysc_vtime: sysc_vtime:
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
...@@ -260,14 +259,14 @@ sysc_update: ...@@ -260,14 +259,14 @@ sysc_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
LAST_BREAK LAST_BREAK
sysc_do_svc: 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 slag %r7,%r7,2 # shift and test for svc 0
jnz sysc_nr_ok jnz sysc_nr_ok
# svc 0: system call number in %r1 # svc 0: system call number in %r1
llgfr %r1,%r1 # clear high word in r1 llgfr %r1,%r1 # clear high word in r1
cghi %r1,NR_syscalls cghi %r1,NR_syscalls
jnl sysc_nr_ok 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 slag %r7,%r1,2 # shift and test for svc 0
sysc_nr_ok: sysc_nr_ok:
larl %r10,sys_call_table larl %r10,sys_call_table
...@@ -358,11 +357,12 @@ sysc_notify_resume: ...@@ -358,11 +357,12 @@ sysc_notify_resume:
# #
sysc_restart: sysc_restart:
ni __TI_flags+7(%r12),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC 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
lmg %r2,%r6,SP_R2(%r15) # load svc arguments lmg %r2,%r6,SP_R2(%r15) # load svc arguments
sth %r7,SP_SVCNR(%r15) lghi %r7,0 # svc 0 returns -ENOSYS
slag %r7,%r7,2 lh %r1,SP_SVC_CODE+2(%r15) # load new svc number
cghi %r1,NR_syscalls
jnl sysc_nr_ok # invalid svc number -> do svc 0
slag %r7,%r1,2
j sysc_nr_ok # restart svc j sysc_nr_ok # restart svc
# #
...@@ -370,7 +370,7 @@ sysc_restart: ...@@ -370,7 +370,7 @@ sysc_restart:
# #
sysc_singlestep: sysc_singlestep:
ni __TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP 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 la %r2,SP_PTREGS(%r15) # address of register-save area
larl %r14,sysc_return # load adr. of system return larl %r14,sysc_return # load adr. of system return
jg do_per_trap jg do_per_trap
...@@ -382,7 +382,7 @@ sysc_singlestep: ...@@ -382,7 +382,7 @@ sysc_singlestep:
sysc_tracesys: sysc_tracesys:
la %r2,SP_PTREGS(%r15) # load pt_regs la %r2,SP_PTREGS(%r15) # load pt_regs
la %r3,0 la %r3,0
llgh %r0,SP_SVCNR(%r15) llgh %r0,SP_SVC_CODE+2(%r15)
stg %r0,SP_R2(%r15) stg %r0,SP_R2(%r15)
brasl %r14,do_syscall_trace_enter brasl %r14,do_syscall_trace_enter
lghi %r0,NR_syscalls lghi %r0,NR_syscalls
...@@ -470,7 +470,7 @@ ENTRY(pgm_check_handler) ...@@ -470,7 +470,7 @@ ENTRY(pgm_check_handler)
jnz pgm_per # got per exception -> special case jnz pgm_per # got per exception -> special case
SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_PGM __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __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 mvc SP_PSW(16,%r15),__LC_PGM_OLD_PSW
lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct lg %r12,__LC_THREAD_INFO # load pointer to thread_info struct
HANDLE_SIE_INTERCEPT HANDLE_SIE_INTERCEPT
...@@ -551,7 +551,7 @@ pgm_svcper: ...@@ -551,7 +551,7 @@ pgm_svcper:
SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA SAVE_ALL_PGM __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW 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 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_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
...@@ -571,7 +571,7 @@ pgm_svcper: ...@@ -571,7 +571,7 @@ pgm_svcper:
# #
kernel_per: kernel_per:
REENABLE_IRQS 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 la %r2,SP_PTREGS(%r15) # address of register-save area
brasl %r14,do_per_trap brasl %r14,do_per_trap
j pgm_exit j pgm_exit
...@@ -973,7 +973,7 @@ cleanup_system_call: ...@@ -973,7 +973,7 @@ cleanup_system_call:
stg %r11,0(%r12) stg %r11,0(%r12)
CREATE_STACK_FRAME __LC_SAVE_AREA CREATE_STACK_FRAME __LC_SAVE_AREA
mvc SP_PSW(16,%r15),__LC_SVC_OLD_PSW 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 mvc 8(8,%r12),__LC_THREAD_INFO
cleanup_vtime: cleanup_vtime:
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24) clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24)
......
...@@ -42,6 +42,7 @@ enum s390_regset { ...@@ -42,6 +42,7 @@ enum s390_regset {
REGSET_GENERAL, REGSET_GENERAL,
REGSET_FP, REGSET_FP,
REGSET_LAST_BREAK, REGSET_LAST_BREAK,
REGSET_SYSTEM_CALL,
REGSET_GENERAL_EXTENDED, REGSET_GENERAL_EXTENDED,
}; };
...@@ -303,6 +304,13 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) ...@@ -303,6 +304,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 */ high order bit but older gdb's rely on it */
data |= PSW_ADDR_AMODE; data |= PSW_ADDR_AMODE;
#endif #endif
if (addr == (addr_t) &dummy->regs.psw.addr)
/*
* The debugger changed the instruction address,
* reset system call restart, see signal.c:do_signal
*/
task_thread_info(child)->system_call = 0;
*(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data; *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
...@@ -610,6 +618,11 @@ static int __poke_user_compat(struct task_struct *child, ...@@ -610,6 +618,11 @@ static int __poke_user_compat(struct task_struct *child,
/* Build a 64 bit psw address from 31 bit address. */ /* Build a 64 bit psw address from 31 bit address. */
task_pt_regs(child)->psw.addr = task_pt_regs(child)->psw.addr =
(__u64) tmp & PSW32_ADDR_INSN; (__u64) tmp & PSW32_ADDR_INSN;
/*
* The debugger changed the instruction address,
* reset system call restart, see signal.c:do_signal
*/
task_thread_info(child)->system_call = 0;
} else { } else {
/* gpr 0-15 */ /* gpr 0-15 */
*(__u32*)((addr_t) &task_pt_regs(child)->psw *(__u32*)((addr_t) &task_pt_regs(child)->psw
...@@ -737,7 +750,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) ...@@ -737,7 +750,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
* debugger stored an invalid system call number. Skip * debugger stored an invalid system call number. Skip
* the system call and the system call restart handling. * the system call and the system call restart handling.
*/ */
regs->svcnr = 0; regs->svc_code = 0;
ret = -1; ret = -1;
} }
...@@ -899,6 +912,26 @@ static int s390_last_break_get(struct task_struct *target, ...@@ -899,6 +912,26 @@ static int s390_last_break_get(struct task_struct *target,
#endif #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 int *data = &task_thread_info(target)->system_call;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(unsigned int));
}
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 int *data = &task_thread_info(target)->system_call;
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
data, 0, sizeof(unsigned int));
}
static const struct user_regset s390_regsets[] = { static const struct user_regset s390_regsets[] = {
[REGSET_GENERAL] = { [REGSET_GENERAL] = {
.core_note_type = NT_PRSTATUS, .core_note_type = NT_PRSTATUS,
...@@ -925,6 +958,14 @@ static const struct user_regset s390_regsets[] = { ...@@ -925,6 +958,14 @@ static const struct user_regset s390_regsets[] = {
.get = s390_last_break_get, .get = s390_last_break_get,
}, },
#endif #endif
[REGSET_SYSTEM_CALL] = {
.core_note_type = NT_S390_SYSTEM_CALL,
.n = 1,
.size = sizeof(unsigned int),
.align = sizeof(unsigned int),
.get = s390_system_call_get,
.set = s390_system_call_set,
},
}; };
static const struct user_regset_view user_s390_view = { static const struct user_regset_view user_s390_view = {
...@@ -1104,6 +1145,14 @@ static const struct user_regset s390_compat_regsets[] = { ...@@ -1104,6 +1145,14 @@ static const struct user_regset s390_compat_regsets[] = {
.align = sizeof(long), .align = sizeof(long),
.get = s390_compat_last_break_get, .get = s390_compat_last_break_get,
}, },
[REGSET_SYSTEM_CALL] = {
.core_note_type = NT_S390_SYSTEM_CALL,
.n = 1,
.size = sizeof(compat_uint_t),
.align = sizeof(compat_uint_t),
.get = s390_system_call_get,
.set = s390_system_call_set,
},
[REGSET_GENERAL_EXTENDED] = { [REGSET_GENERAL_EXTENDED] = {
.core_note_type = NT_S390_HIGH_GPRS, .core_note_type = NT_S390_HIGH_GPRS,
.n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/compat.h>
#include "entry.h" #include "entry.h"
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
...@@ -156,7 +157,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -156,7 +157,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
current->thread.fp_regs.fpc &= FPC_VALID_MASK; current->thread.fp_regs.fpc &= FPC_VALID_MASK;
restore_fp_regs(&current->thread.fp_regs); restore_fp_regs(&current->thread.fp_regs);
regs->svcnr = 0; /* disable syscall checks */ regs->svc_code = 0; /* disable syscall checks */
return 0; return 0;
} }
...@@ -401,7 +402,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -401,7 +402,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
*/ */
void do_signal(struct pt_regs *regs) void do_signal(struct pt_regs *regs)
{ {
unsigned long retval = 0, continue_addr = 0, restart_addr = 0;
siginfo_t info; siginfo_t info;
int signr; int signr;
struct k_sigaction ka; struct k_sigaction ka;
...@@ -421,54 +421,43 @@ void do_signal(struct pt_regs *regs) ...@@ -421,54 +421,43 @@ void do_signal(struct pt_regs *regs)
else else
oldset = &current->blocked; oldset = &current->blocked;
/* Are we from a system call? */ /*
if (regs->svcnr) { * Get signal to deliver. When running under ptrace, at this point
continue_addr = regs->psw.addr; * the debugger may change all our registers, including the system
restart_addr = continue_addr - regs->ilc; * call information.
retval = regs->gprs[2]; */
current_thread_info()->system_call = regs->svc_code;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
regs->svc_code = current_thread_info()->system_call;
/* Prepare for system call restart. We do this here so that a if (signr > 0) {
debugger will see the already changed PSW. */ /* Whee! Actually deliver the signal. */
switch (retval) { if (regs->svc_code > 0) {
/* Check for system call restarting. */
switch (regs->gprs[2]) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
regs->gprs[2] = -EINTR;
break;
case -ERESTARTSYS: case -ERESTARTSYS:
if (!(ka.sa.sa_flags & SA_RESTART)) {
regs->gprs[2] = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR: case -ERESTARTNOINTR:
regs->gprs[2] = regs->orig_gpr2; regs->gprs[2] = regs->orig_gpr2;
regs->psw.addr = restart_addr; regs->psw.addr = regs->psw.addr -
(regs->svc_code >> 16);
break; 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 ... */
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;
} }
/* No longer in a system call */
regs->svc_code = 0;
} }
if (signr > 0) { if ((is_compat_task() ?
/* Whee! Actually deliver the signal. */ handle_signal32(signr, &ka, &info, oldset, regs) :
int ret; handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
#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) {
/* /*
* A signal was successfully delivered; the saved * A signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame, * sigmask will have been stored in the signal frame,
...@@ -487,6 +476,23 @@ void do_signal(struct pt_regs *regs) ...@@ -487,6 +476,23 @@ void do_signal(struct pt_regs *regs)
return; 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. * 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) ...@@ -494,13 +500,6 @@ void do_signal(struct pt_regs *regs)
clear_thread_flag(TIF_RESTORE_SIGMASK); clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL); 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) void do_notify_resume(struct pt_regs *regs)
......
...@@ -395,6 +395,7 @@ typedef struct elf64_shdr { ...@@ -395,6 +395,7 @@ typedef struct elf64_shdr {
#define NT_S390_CTRS 0x304 /* s390 control registers */ #define NT_S390_CTRS 0x304 /* s390 control registers */
#define NT_S390_PREFIX 0x305 /* s390 prefix register */ #define NT_S390_PREFIX 0x305 /* s390 prefix register */
#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ #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 */ #define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment