aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2012-07-04 13:17:16 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-07-05 04:50:56 -0400
commit433e2f307beff8adba241646ce9108544e0c5a03 (patch)
tree8f816b84bae19872ade5db31fe3d6f223dc52675 /arch
parent3b0c06226783ffc836217eb34f7eca311b1e63f7 (diff)
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 <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/include/asm/thread_info.h5
-rw-r--r--arch/arm/kernel/ptrace.c3
-rw-r--r--arch/arm/kernel/signal.c33
3 files changed, 28 insertions, 13 deletions
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index b79f8e97f775..af7b0bda3355 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -148,7 +148,6 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
148#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ 148#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
149#define TIF_SYSCALL_TRACE 8 149#define TIF_SYSCALL_TRACE 8
150#define TIF_SYSCALL_AUDIT 9 150#define TIF_SYSCALL_AUDIT 9
151#define TIF_SYSCALL_RESTARTSYS 10
152#define TIF_POLLING_NRFLAG 16 151#define TIF_POLLING_NRFLAG 16
153#define TIF_USING_IWMMXT 17 152#define TIF_USING_IWMMXT 17
154#define TIF_MEMDIE 18 /* is terminating due to OOM killer */ 153#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
@@ -164,11 +163,9 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *,
164#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) 163#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
165#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) 164#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
166#define _TIF_SECCOMP (1 << TIF_SECCOMP) 165#define _TIF_SECCOMP (1 << TIF_SECCOMP)
167#define _TIF_SYSCALL_RESTARTSYS (1 << TIF_SYSCALL_RESTARTSYS)
168 166
169/* Checks for any syscall work in entry-common.S */ 167/* Checks for any syscall work in entry-common.S */
170#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ 168#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT)
171 _TIF_SYSCALL_RESTARTSYS)
172 169
173/* 170/*
174 * Change these and you break ASM code in entry-common.S 171 * Change these and you break ASM code in entry-common.S
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 5700a7ae7f0b..14e38261cd31 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -25,7 +25,6 @@
25#include <linux/regset.h> 25#include <linux/regset.h>
26#include <linux/audit.h> 26#include <linux/audit.h>
27#include <linux/tracehook.h> 27#include <linux/tracehook.h>
28#include <linux/unistd.h>
29 28
30#include <asm/pgtable.h> 29#include <asm/pgtable.h>
31#include <asm/traps.h> 30#include <asm/traps.h>
@@ -918,8 +917,6 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
918 audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0, 917 audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0,
919 regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); 918 regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);
920 919
921 if (why == 0 && test_and_clear_thread_flag(TIF_SYSCALL_RESTARTSYS))
922 scno = __NR_restart_syscall - __NR_SYSCALL_BASE;
923 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 920 if (!test_thread_flag(TIF_SYSCALL_TRACE))
924 return scno; 921 return scno;
925 922
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)
605 case -ERESTARTNOHAND: 605 case -ERESTARTNOHAND:
606 case -ERESTARTSYS: 606 case -ERESTARTSYS:
607 case -ERESTARTNOINTR: 607 case -ERESTARTNOINTR:
608 case -ERESTART_RESTARTBLOCK:
609 regs->ARM_r0 = regs->ARM_ORIG_r0; 608 regs->ARM_r0 = regs->ARM_ORIG_r0;
610 regs->ARM_pc = restart_addr; 609 regs->ARM_pc = restart_addr;
611 break; 610 break;
611 case -ERESTART_RESTARTBLOCK:
612 regs->ARM_r0 = -EINTR;
613 break;
612 } 614 }
613 } 615 }
614 616
@@ -624,14 +626,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
624 * debugger has chosen to restart at a different PC. 626 * debugger has chosen to restart at a different PC.
625 */ 627 */
626 if (regs->ARM_pc == restart_addr) { 628 if (regs->ARM_pc == restart_addr) {
627 if (retval == -ERESTARTNOHAND || 629 if (retval == -ERESTARTNOHAND
628 retval == -ERESTART_RESTARTBLOCK
629 || (retval == -ERESTARTSYS 630 || (retval == -ERESTARTSYS
630 && !(ka.sa.sa_flags & SA_RESTART))) { 631 && !(ka.sa.sa_flags & SA_RESTART))) {
631 regs->ARM_r0 = -EINTR; 632 regs->ARM_r0 = -EINTR;
632 regs->ARM_pc = continue_addr; 633 regs->ARM_pc = continue_addr;
633 } 634 }
634 clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
635 } 635 }
636 636
637 handle_signal(signr, &ka, &info, regs); 637 handle_signal(signr, &ka, &info, regs);
@@ -645,8 +645,29 @@ static void do_signal(struct pt_regs *regs, int syscall)
645 * ignore the restart. 645 * ignore the restart.
646 */ 646 */
647 if (retval == -ERESTART_RESTARTBLOCK 647 if (retval == -ERESTART_RESTARTBLOCK
648 && regs->ARM_pc == restart_addr) 648 && regs->ARM_pc == continue_addr) {
649 set_thread_flag(TIF_SYSCALL_RESTARTSYS); 649 if (thumb_mode(regs)) {
650 regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
651 regs->ARM_pc -= 2;
652 } else {
653#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
654 regs->ARM_r7 = __NR_restart_syscall;
655 regs->ARM_pc -= 4;
656#else
657 u32 __user *usp;
658
659 regs->ARM_sp -= 4;
660 usp = (u32 __user *)regs->ARM_sp;
661
662 if (put_user(regs->ARM_pc, usp) == 0) {
663 regs->ARM_pc = KERN_RESTART_CODE;
664 } else {
665 regs->ARM_sp += 4;
666 force_sigsegv(0, current);
667 }
668#endif
669 }
670 }
650 } 671 }
651 672
652 restore_saved_sigmask(); 673 restore_saved_sigmask();