FAILED: patch "[PATCH] s390/signal: switch to using vdso for sigreturn and syscall" failed to apply to 5.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From df29a7440c4b5c65765c8f60396b3b13063e24e9 Mon Sep 17 00:00:00 2001
From: Sven Schnelle <[email protected]>
Date: Fri, 25 Jun 2021 15:02:08 +0200
Subject: [PATCH] s390/signal: switch to using vdso for sigreturn and syscall
restart
with generic entry, there's a bug when it comes to restarting of signals. The failing sequence is:
a) a signal is coming in, and no handler is registered, so the lower part of arch_do_signal_or_restart() in arch/s390/kernel/signal.c sets PIF_SYSCALL_RESTART.
b) a second signal gets pending while the kernel is still in the exit loop, and for that one, a handler exists.
c) The first part of arch_do_signal_or_restart() is called. That part calls handle_signal(), which sets up stack + registers for handling the signal.
d) __do_syscall() in arch/s390/kernel/syscall.c checks for PIF_SYSCALL_RESTART right before leaving to userspace. If it is set, it restart's the syscall. However, the registers are already setup for handling a signal from c). The syscall is now restarted with the wrong arguments.
Change the code to:
- use vdso for syscall_restart() instead of PIF_SYSCALL_RESTART because we cannot rewind and go back to userspace on s390 because the system call number might be encoded in the svc instruction.
- for all other syscalls we rewind the PSW and return to userspace.
arch/s390/kernel/compat_signal.c +3 -10
@@ -28,6 +28,7 @@#include <linux/uaccess.h> #include <asm/lowcore.h> #include <asm/switch_to.h> +#include <asm/vdso.h> #include "compat_linux.h" #include "compat_ptrace.h" #include "entry.h"
@@ -118,7 +119,6 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)fpregs_load ((_s390_fp_regs *) &user_sregs.fpregs, ¤t->thread.fpu);clear_pt_regs_flag (regs,PIF_SYSCALL );/* No longer in a system call */ -clear_pt_regs_flag (regs,PIF_SYSCALL_RESTART );return 0 ; }
@@ -304,11 +304,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, restorer = (unsigned long __force ) ksig->ka.sa.sa_restorer |PSW32_ADDR_AMODE ; }else { -/* Signal frames without vectors registers are short ! */ -__u16 __user *svc = (void __user *) frame + frame_size -2 ; -if (__put_user (S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) -return -EFAULT ; - restorer = (unsigned long __force ) svc |PSW32_ADDR_AMODE ; + restorer =VDSO32_SYMBOL (current, sigreturn); }/* Set up registers for signal handler */
@@ -371,10 +367,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, restorer = (unsigned long __force ) ksig->ka.sa.sa_restorer |PSW32_ADDR_AMODE ; }else { -__u16 __user *svc = &frame->svc_insn; -if (__put_user (S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) -return -EFAULT ; - restorer = (unsigned long __force ) svc |PSW32_ADDR_AMODE ; + restorer =VDSO32_SYMBOL (current, rt_sigreturn); }/* Create siginfo on the signal stack */
arch/s390/kernel/process.c +6 -0
@@ -166,6 +166,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, p->thread.acrs[1 ] = (unsigned int )tls; } } +/* +* s390 stores the svc return address in arch_data when calling +* sigreturn()/restart_syscall() via vdso. 1 means no valid address +* stored. +*/ + p->restart_block.arch_data =1 ;return 0 ; }
arch/s390/kernel/signal.c +18 -21
@@ -32,6 +32,7 @@#include <linux/uaccess.h> #include <asm/lowcore.h> #include <asm/switch_to.h> +#include <asm/vdso.h> #include "entry.h" /*
@@ -171,7 +172,6 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)fpregs_load (&user_sregs.fpregs, ¤t->thread.fpu);clear_pt_regs_flag (regs,PIF_SYSCALL );/* No longer in a system call */ -clear_pt_regs_flag (regs,PIF_SYSCALL_RESTART );return 0 ; }
@@ -334,15 +334,10 @@ static int setup_frame(int sig, struct k_sigaction *ka,/* Set up to return from userspace. If provided, use a stub already in userspace. */ -if (ka->sa.sa_flags &SA_RESTORER ) { +if (ka->sa.sa_flags &SA_RESTORER ) restorer = (unsigned long ) ka->sa.sa_restorer; - }else { -/* Signal frame without vector registers are short ! */ -__u16 __user *svc = (void __user *) frame + frame_size -2 ; -if (__put_user (S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) -return -EFAULT ; - restorer = (unsigned long ) svc; - } +else + restorer =VDSO64_SYMBOL (current, sigreturn);/* Set up registers for signal handler */ regs->gprs[14 ] = restorer;
@@ -397,14 +392,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,/* Set up to return from userspace. If provided, use a stub already in userspace. */ -if (ksig->ka.sa.sa_flags &SA_RESTORER ) { +if (ksig->ka.sa.sa_flags &SA_RESTORER ) restorer = (unsigned long ) ksig->ka.sa.sa_restorer; - }else { -__u16 __user *svc = &frame->svc_insn; -if (__put_user (S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) -return -EFAULT ; - restorer = (unsigned long ) svc; - } +else + restorer =VDSO64_SYMBOL (current, rt_sigreturn);/* Create siginfo on the signal stack */ if (copy_siginfo_to_user (&frame->info, &ksig->info))
@@ -501,7 +492,7 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) }/* No longer in a system call */ clear_pt_regs_flag (regs,PIF_SYSCALL ); -clear_pt_regs_flag (regs,PIF_SYSCALL_RESTART ); +rseq_signal_deliver (&ksig, regs);if (is_compat_task ())handle_signal32 (&ksig, oldset, regs);
@@ -517,14 +508,20 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)switch (regs->gprs[2 ]) {case -ERESTART_RESTARTBLOCK :/* Restart with sys_restart_syscall */ - regs->int_code = __NR_restart_syscall; - fallthrough; + regs->gprs[2 ] = regs->orig_gpr2; + current->restart_block.arch_data = regs->psw.addr; +if (is_compat_task ()) + regs->psw.addr =VDSO32_SYMBOL (current, restart_syscall); +else + regs->psw.addr =VDSO64_SYMBOL (current, restart_syscall); +if (test_thread_flag (TIF_SINGLE_STEP )) +clear_thread_flag (TIF_PER_TRAP ); +break ;case -ERESTARTNOHAND :case -ERESTARTSYS :case -ERESTARTNOINTR : -/* Restart system call with magic TIF bit. */ regs->gprs[2 ] = regs->orig_gpr2; -set_pt_regs_flag (regs,PIF_SYSCALL_RESTART ); + regs->psw.addr =__rewind_psw (regs->psw, regs->int_code >>16 );if (test_thread_flag (TIF_SINGLE_STEP ))clear_thread_flag (TIF_PER_TRAP );break ;
arch/s390/kernel/syscall.c +4 -0
@@ -121,6 +121,10 @@ void do_syscall(struct pt_regs *regs) regs->gprs[2 ] = nr; +if (nr == __NR_restart_syscall && !(current->restart_block.arch_data &1 )) { + regs->psw.addr = current->restart_block.arch_data; + current->restart_block.arch_data =1 ; + } nr =syscall_enter_from_user_mode_work (regs, nr);/*