From: Jerome Marchand <jmarchan@redhat.com> Date: Tue, 24 Jun 2008 14:56:07 +0200 Subject: [x86_64] ia32 syscall restart fix Message-id: 4860EEE7.3020307@redhat.com O-Subject: [RHEL5.3 Patch] x86_64: ia32 syscall restart fix Bugzilla: 434998 RH-Acked-by: David S. Miller <davem@redhat.com> RH-Acked-by: Chuck Ebbert <cebbert@redhat.com> RH-Acked-by: Roland McGrath <roland@redhat.com> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=434998 Description: The code to restart syscalls after signals depends on checking for a negative orig_rax, and for particular negative -ERESTART* values in rax. These fields are 64 bits and for a 32-bit task they get zero-extended, therefore they are never negative and syscall restart behavior is lost. Solution: Doing sign-extension where it matters. For orig_rax, the only time the value should be -1 but winds up as 0x0ffffffff is via a 32-bit ptrace call. So the patch changes ptrace to sign-extend the 32-bit orig_eax value when it's stored; it doesn't change the checks on orig_rax, though it uses the new current_syscall() inline to better document the subtle importance of the used of signedness there. The rax value is stored a lot of ways and it seems hard to get them all sign-extended at their origins. So for that, we use the current_syscall_ret() to sign-extend it only for 32-bit tasks at the time of the -ERESTART* comparisons. Upstream status: commit 40f0933d51f4cba26a5c009a26bb230f4514c1b6 Test status: Built on all arch and tested on x86_64 using the reproducer provided in bugzilla. Brew build: http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1365391 Regards, Jerome diff --git a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c index 2de8d7b..8a5dad9 100644 --- a/arch/x86_64/ia32/ptrace32.c +++ b/arch/x86_64/ia32/ptrace32.c @@ -86,10 +86,18 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 val) R32(esi, rsi); R32(ebp, rbp); R32(eax, rax); - R32(orig_eax, orig_rax); R32(eip, rip); R32(esp, rsp); + case offsetof(struct user_regs_struct32, orig_eax): { + /* + * Sign-extend the value so that orig_eax = -1 + * causes (long)orig_rax < 0 tests to fire correctly. + */ + stack[offsetof(struct pt_regs, orig_rax)/8] = (long) (s32) val; + break; + } + case offsetof(struct user_regs_struct32, eflags): { __u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8]; val &= FLAG_MASK; diff --git a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c index 54573ca..0481e0a 100644 --- a/arch/x86_64/kernel/signal.c +++ b/arch/x86_64/kernel/signal.c @@ -315,6 +315,35 @@ give_sigsegv: } /* + * Return -1L or the syscall number that @regs is executing. + */ +static long current_syscall(struct pt_regs *regs) +{ + /* + * We always sign-extend a -1 value being set here, + * so this is always either -1L or a syscall number. + */ + return regs->orig_rax; +} + +/* + * Return a value that is -EFOO if the system call in @regs->orig_rax + * returned an error. This only works for @regs from @current. + */ +static long current_syscall_ret(struct pt_regs *regs) +{ +#ifdef CONFIG_IA32_EMULATION + if (test_thread_flag(TIF_IA32)) + /* + * Sign-extend the value so (int)-EFOO becomes (long)-EFOO + * and will match correctly in comparisons. + */ + return (int) regs->rax; +#endif + return regs->rax; +} + +/* * OK, we're invoking a handler */ @@ -331,9 +360,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, #endif /* Are we from a system call? */ - if ((long)regs->orig_rax >= 0) { + if (current_syscall(regs) >= 0) { /* If so, check system call restarting.. */ - switch (regs->rax) { + switch (current_syscall_ret(regs)) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->rax = -EINTR; @@ -439,10 +468,9 @@ static void do_signal(struct pt_regs *regs) } /* Did we come from a system call? */ - if ((long)regs->orig_rax >= 0) { + if (current_syscall(regs) >= 0) { /* Restart the system call - no handlers present */ - long res = regs->rax; - switch (res) { + switch (current_syscall_ret(regs)) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: