aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
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/arm/kernel
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/arm/kernel')
-rw-r--r--arch/arm/kernel/ptrace.c3
-rw-r--r--arch/arm/kernel/signal.c33
2 files changed, 27 insertions, 9 deletions
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();