From 3b0c06226783ffc836217eb34f7eca311b1e63f7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 4 Jul 2012 18:16:30 +0100 Subject: ARM: 7442/1: Revert "remove unused restart trampoline" This reverts commit fa18484d0947b976a769d15c83c50617493c81c1. We need the restart trampoline back so that we can revert a related problematic patch 6b5c8045ecc7e726cdaa2a9d9c8e5008050e1252 ("arm: new way of handling ERESTART_RESTARTBLOCK"). Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index fd2392a17ac1..6d3bce5bd7bc 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -27,6 +27,7 @@ */ #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) +#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE) /* * With EABI, the syscall number has to be loaded into r7. @@ -46,6 +47,18 @@ const unsigned long sigreturn_codes[7] = { MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, }; +/* + * Either we support OABI only, or we have EABI with the OABI + * compat layer enabled. In the later case we don't know if + * user space is EABI or not, and if not we must not clobber r7. + * Always using the OABI syscall solves that issue and works for + * all those cases. + */ +const unsigned long syscall_restart_code[2] = { + SWI_SYS_RESTART, /* swi __NR_restart_syscall */ + 0xe49df004, /* ldr pc, [sp], #4 */ +}; + /* * atomically swap in the new signal mask, and wait for a signal. */ -- cgit v1.2.3 From 433e2f307beff8adba241646ce9108544e0c5a03 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 4 Jul 2012 18:17:16 +0100 Subject: ARM: 7443/1: Revert "new way of handling ERESTART_RESTARTBLOCK" This reverts commit 6b5c8045ecc7e726cdaa2a9d9c8e5008050e1252. Conflicts: arch/arm/kernel/ptrace.c The new syscall restarting code can lead to problems if we take an interrupt in userspace just before restarting the svc instruction. If a signal is delivered when returning from the interrupt, the TIF_SYSCALL_RESTARTSYS will remain set and cause any syscalls executed from the signal handler to be treated as a restart of the previously interrupted system call. This includes the final sigreturn call, meaning that we may fail to exit from the signal context. Furthermore, if a system call made from the signal handler requires a restart via the restart_block, it is possible to clear the thread flag and fail to restart the originally interrupted system call. The right solution to this problem is to perform the restarting in the kernel, avoiding the possibility of handling a further signal before the restart is complete. Since we're almost at -rc6, let's revert the new method for now and aim for in-kernel restarting at a later date. Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 6d3bce5bd7bc..536c5d6b340b 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -605,10 +605,12 @@ static void do_signal(struct pt_regs *regs, int syscall) case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: - case -ERESTART_RESTARTBLOCK: regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc = restart_addr; break; + case -ERESTART_RESTARTBLOCK: + regs->ARM_r0 = -EINTR; + break; } } @@ -624,14 +626,12 @@ static void do_signal(struct pt_regs *regs, int syscall) * debugger has chosen to restart at a different PC. */ if (regs->ARM_pc == restart_addr) { - if (retval == -ERESTARTNOHAND || - retval == -ERESTART_RESTARTBLOCK + if (retval == -ERESTARTNOHAND || (retval == -ERESTARTSYS && !(ka.sa.sa_flags & SA_RESTART))) { regs->ARM_r0 = -EINTR; regs->ARM_pc = continue_addr; } - clear_thread_flag(TIF_SYSCALL_RESTARTSYS); } handle_signal(signr, &ka, &info, regs); @@ -645,8 +645,29 @@ static void do_signal(struct pt_regs *regs, int syscall) * ignore the restart. */ if (retval == -ERESTART_RESTARTBLOCK - && regs->ARM_pc == restart_addr) - set_thread_flag(TIF_SYSCALL_RESTARTSYS); + && regs->ARM_pc == continue_addr) { + if (thumb_mode(regs)) { + regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; + regs->ARM_pc -= 2; + } else { +#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) + regs->ARM_r7 = __NR_restart_syscall; + regs->ARM_pc -= 4; +#else + u32 __user *usp; + + regs->ARM_sp -= 4; + usp = (u32 __user *)regs->ARM_sp; + + if (put_user(regs->ARM_pc, usp) == 0) { + regs->ARM_pc = KERN_RESTART_CODE; + } else { + regs->ARM_sp += 4; + force_sigsegv(0, current); + } +#endif + } + } } restore_saved_sigmask(); -- cgit v1.2.3 From ad82cc08f70486b5741560b1b2121dadf82897de Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 19 Jul 2012 17:46:44 +0100 Subject: ARM: 7470/1: Revert "7443/1: Revert "new way of handling ERESTART_RESTARTBLOCK"" This reverts commit 433e2f307beff8adba241646ce9108544e0c5a03. Conflicts: arch/arm/kernel/ptrace.c Reintroduce the new syscall restart handling in preparation for further patches from Al Viro. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 536c5d6b340b..6d3bce5bd7bc 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -605,12 +605,10 @@ static void do_signal(struct pt_regs *regs, int syscall) case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: + case -ERESTART_RESTARTBLOCK: regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc = restart_addr; break; - case -ERESTART_RESTARTBLOCK: - regs->ARM_r0 = -EINTR; - break; } } @@ -626,12 +624,14 @@ static void do_signal(struct pt_regs *regs, int syscall) * debugger has chosen to restart at a different PC. */ if (regs->ARM_pc == restart_addr) { - if (retval == -ERESTARTNOHAND + if (retval == -ERESTARTNOHAND || + retval == -ERESTART_RESTARTBLOCK || (retval == -ERESTARTSYS && !(ka.sa.sa_flags & SA_RESTART))) { regs->ARM_r0 = -EINTR; regs->ARM_pc = continue_addr; } + clear_thread_flag(TIF_SYSCALL_RESTARTSYS); } handle_signal(signr, &ka, &info, regs); @@ -645,29 +645,8 @@ static void do_signal(struct pt_regs *regs, int syscall) * ignore the restart. */ if (retval == -ERESTART_RESTARTBLOCK - && regs->ARM_pc == continue_addr) { - if (thumb_mode(regs)) { - regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; - regs->ARM_pc -= 2; - } else { -#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) - regs->ARM_r7 = __NR_restart_syscall; - regs->ARM_pc -= 4; -#else - u32 __user *usp; - - regs->ARM_sp -= 4; - usp = (u32 __user *)regs->ARM_sp; - - if (put_user(regs->ARM_pc, usp) == 0) { - regs->ARM_pc = KERN_RESTART_CODE; - } else { - regs->ARM_sp += 4; - force_sigsegv(0, current); - } -#endif - } - } + && regs->ARM_pc == restart_addr) + set_thread_flag(TIF_SYSCALL_RESTARTSYS); } restore_saved_sigmask(); -- cgit v1.2.3 From 8d4150ccbb63419649a69fe04b46120a46d60da2 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 19 Jul 2012 17:47:22 +0100 Subject: ARM: 7471/1: Revert "7442/1: Revert "remove unused restart trampoline"" This reverts commit 3b0c06226783ffc836217eb34f7eca311b1e63f7. We no longer require the restart trampoline for syscall restarting. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 6d3bce5bd7bc..fd2392a17ac1 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -27,7 +27,6 @@ */ #define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE)) #define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE)) -#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE) /* * With EABI, the syscall number has to be loaded into r7. @@ -47,18 +46,6 @@ const unsigned long sigreturn_codes[7] = { MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, }; -/* - * Either we support OABI only, or we have EABI with the OABI - * compat layer enabled. In the later case we don't know if - * user space is EABI or not, and if not we must not clobber r7. - * Always using the OABI syscall solves that issue and works for - * all those cases. - */ -const unsigned long syscall_restart_code[2] = { - SWI_SYS_RESTART, /* swi __NR_restart_syscall */ - 0xe49df004, /* ldr pc, [sp], #4 */ -}; - /* * atomically swap in the new signal mask, and wait for a signal. */ -- cgit v1.2.3 From 0a267fa6a15d41c4061358bf2e67c633fdbffc90 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jul 2012 17:47:55 +0100 Subject: ARM: 7472/1: pull all work_pending logics into C function Signed-off-by: Al Viro Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index fd2392a17ac1..8756e5dcd377 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -640,13 +640,24 @@ static void do_signal(struct pt_regs *regs, int syscall) } asmlinkage void -do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) +do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) { - if (thread_flags & _TIF_SIGPENDING) - do_signal(regs, syscall); - - if (thread_flags & _TIF_NOTIFY_RESUME) { - clear_thread_flag(TIF_NOTIFY_RESUME); - tracehook_notify_resume(regs); - } + do { + if (likely(thread_flags & _TIF_NEED_RESCHED)) { + schedule(); + } else { + if (unlikely(!user_mode(regs))) + return; + local_irq_enable(); + if (thread_flags & _TIF_SIGPENDING) { + do_signal(regs, syscall); + syscall = 0; + } else { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } + } + local_irq_disable(); + thread_flags = current_thread_info()->flags; + } while (thread_flags & _TIF_WORK_MASK); } -- cgit v1.2.3 From 81783786d5cf4aa0d3e15bb0fac856aa8ebf1a76 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jul 2012 17:48:21 +0100 Subject: ARM: 7473/1: deal with handlerless restarts without leaving the kernel Signed-off-by: Al Viro Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 50 ++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 8756e5dcd377..99851cb9591d 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -static void do_signal(struct pt_regs *regs, int syscall) +static int do_signal(struct pt_regs *regs, int syscall) { unsigned int retval = 0, continue_addr = 0, restart_addr = 0; struct k_sigaction ka; siginfo_t info; int signr; + int restart = 0; /* * If we were from a system call, check for system call restarting... @@ -589,10 +590,12 @@ static void do_signal(struct pt_regs *regs, int syscall) * debugger will see the already changed PSW. */ switch (retval) { + case -ERESTART_RESTARTBLOCK: + restart++; case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: - case -ERESTART_RESTARTBLOCK: + restart++; regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc = restart_addr; break; @@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall) * 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. But skip this if a + * debugger has chosen to restart at a different PC. + */ + if (regs->ARM_pc != restart_addr) + restart = 0; if (signr > 0) { - /* - * Depending on the signal settings we may need to revert the - * decision to restart the system call. But skip this if a - * debugger has chosen to restart at a different PC. - */ - if (regs->ARM_pc == restart_addr) { + if (unlikely(restart)) { if (retval == -ERESTARTNOHAND || retval == -ERESTART_RESTARTBLOCK || (retval == -ERESTARTSYS @@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall) regs->ARM_r0 = -EINTR; regs->ARM_pc = continue_addr; } - clear_thread_flag(TIF_SYSCALL_RESTARTSYS); } handle_signal(signr, &ka, &info, regs); - return; + return 0; } - if (syscall) { - /* - * Handle restarting a different system call. As above, - * if a debugger has chosen to restart at a different PC, - * ignore the restart. - */ - if (retval == -ERESTART_RESTARTBLOCK - && regs->ARM_pc == restart_addr) + if (unlikely(restart)) { + if (restart > 1) set_thread_flag(TIF_SYSCALL_RESTARTSYS); + regs->ARM_pc = continue_addr; } restore_saved_sigmask(); + return restart; } -asmlinkage void +asmlinkage int do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) { do { @@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) schedule(); } else { if (unlikely(!user_mode(regs))) - return; + return 0; local_irq_enable(); if (thread_flags & _TIF_SIGPENDING) { - do_signal(regs, syscall); + if (unlikely(do_signal(regs, syscall))) { + /* + * Restart without handlers. + * Deal with it without leaving + * the kernel space. + */ + return 1; + } syscall = 0; } else { clear_thread_flag(TIF_NOTIFY_RESUME); @@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) local_irq_disable(); thread_flags = current_thread_info()->flags; } while (thread_flags & _TIF_WORK_MASK); + return 0; } -- cgit v1.2.3 From 6628521784d1da3b7354c6b6e8499e19ab46a3d1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Jul 2012 17:48:50 +0100 Subject: ARM: 7474/1: get rid of TIF_SYSCALL_RESTARTSYS just let do_work_pending() return 1 on normal local restarts and -1 on those that had been caused by ERESTART_RESTARTBLOCK (and 0 is still "all done, sod off to userland now"). And let the asm glue flip scno to restart_syscall(2) one if it got negative from us... [will: resolved conflicts with audit fixes] Signed-off-by: Al Viro Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/signal.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'arch/arm/kernel/signal.c') diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 99851cb9591d..f27789e4e38a 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -591,7 +591,7 @@ static int do_signal(struct pt_regs *regs, int syscall) */ switch (retval) { case -ERESTART_RESTARTBLOCK: - restart++; + restart -= 2; case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: @@ -629,13 +629,9 @@ static int do_signal(struct pt_regs *regs, int syscall) return 0; } - if (unlikely(restart)) { - if (restart > 1) - set_thread_flag(TIF_SYSCALL_RESTARTSYS); - regs->ARM_pc = continue_addr; - } - restore_saved_sigmask(); + if (unlikely(restart)) + regs->ARM_pc = continue_addr; return restart; } @@ -650,13 +646,14 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) return 0; local_irq_enable(); if (thread_flags & _TIF_SIGPENDING) { - if (unlikely(do_signal(regs, syscall))) { + int restart = do_signal(regs, syscall); + if (unlikely(restart)) { /* * Restart without handlers. * Deal with it without leaving * the kernel space. */ - return 1; + return restart; } syscall = 0; } else { -- cgit v1.2.3