aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/include
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2008-02-23 18:23:49 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-23 20:12:15 -0500
commit2f56debd77a8f52f1ac1d3c3d89cc7ce5e083230 (patch)
tree353847d10aa0d5bc1de2707ecd8fa2391ad096f3 /arch/um/include
parente4d06b3f904ddfab4531a1e23f1f5e1bd284b605 (diff)
uml: fix FP register corruption
Commit ee3d9bd4de1ed93d2a7ee41c331ed30a1c7b8acd ("uml: simplify SIGSEGV handling"), while greatly simplifying the kernel SIGSEGV handler that runs in the process address space, introduced a bug which corrupts FP state in the process. Previously, the SIGSEGV handler called the sigreturn system call by hand - it couldn't return through the restorer provided to it because that could try to call the libc restorer which likely wouldn't exist in the process address space. So, it blocked off some signals, including SIGUSR1, on entry to the SIGSEGV handler, queued a SIGUSR1 to itself, and invoked sigreturn. The SIGUSR1 was delivered, and was visible to the UML kernel after sigreturn finished. The commit eliminated the signal masking and the call to sigreturn. The handler simply hits itself with a SIGTRAP to let the UML kernel know that it is finished. UML then restores the process registers, which effectively longjmps the process out of the signal handler, skipping sigreturn's restoring of register state and the signal mask. The bug is that the host apparently sets used_fp to 0 when it saves the process FP state in the sigcontext on the process signal stack. Thus, when the process is longjmped out of the handler, its FP state is corrupt because it wasn't saved on the context switch to the UML kernel. This manifested itself as sleep hanging. For some reason, sleep uses floating point in order to calculate the sleep interval. When a page fault corrupts its FP state, it is faked into essentially sleeping forever. This patch saves the FP state before entering the SIGSEGV handler and restores it afterwards. Signed-off-by: Jeff Dike <jdike@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um/include')
-rw-r--r--arch/um/include/registers.h2
-rw-r--r--arch/um/include/sysdep-i386/ptrace_user.h3
-rw-r--r--arch/um/include/sysdep-x86_64/ptrace_user.h3
3 files changed, 8 insertions, 0 deletions
diff --git a/arch/um/include/registers.h b/arch/um/include/registers.h
index 9ea1ae3c8f46..b0b4589e0ebc 100644
--- a/arch/um/include/registers.h
+++ b/arch/um/include/registers.h
@@ -18,5 +18,7 @@ extern int restore_registers(int pid, struct uml_pt_regs *regs);
18extern int init_registers(int pid); 18extern int init_registers(int pid);
19extern void get_safe_registers(unsigned long *regs); 19extern void get_safe_registers(unsigned long *regs);
20extern unsigned long get_thread_reg(int reg, jmp_buf *buf); 20extern unsigned long get_thread_reg(int reg, jmp_buf *buf);
21extern int get_fp_registers(int pid, unsigned long *regs);
22extern int put_fp_registers(int pid, unsigned long *regs);
21 23
22#endif 24#endif
diff --git a/arch/um/include/sysdep-i386/ptrace_user.h b/arch/um/include/sysdep-i386/ptrace_user.h
index 899aa4b2a78d..75650723c38f 100644
--- a/arch/um/include/sysdep-i386/ptrace_user.h
+++ b/arch/um/include/sysdep-i386/ptrace_user.h
@@ -9,6 +9,7 @@
9#include <sys/ptrace.h> 9#include <sys/ptrace.h>
10#include <linux/ptrace.h> 10#include <linux/ptrace.h>
11#include <asm/ptrace.h> 11#include <asm/ptrace.h>
12#include "user_constants.h"
12 13
13#define PT_OFFSET(r) ((r) * sizeof(long)) 14#define PT_OFFSET(r) ((r) * sizeof(long))
14 15
@@ -40,6 +41,8 @@
40#define PT_SP_OFFSET PT_OFFSET(UESP) 41#define PT_SP_OFFSET PT_OFFSET(UESP)
41#define PT_SP(regs) ((regs)[UESP]) 42#define PT_SP(regs) ((regs)[UESP])
42 43
44#define FP_SIZE ((HOST_XFP_SIZE > HOST_FP_SIZE) ? HOST_XFP_SIZE : HOST_FP_SIZE)
45
43#ifndef FRAME_SIZE 46#ifndef FRAME_SIZE
44#define FRAME_SIZE (17) 47#define FRAME_SIZE (17)
45#endif 48#endif
diff --git a/arch/um/include/sysdep-x86_64/ptrace_user.h b/arch/um/include/sysdep-x86_64/ptrace_user.h
index 4cd61a852fab..45c0bd881cb3 100644
--- a/arch/um/include/sysdep-x86_64/ptrace_user.h
+++ b/arch/um/include/sysdep-x86_64/ptrace_user.h
@@ -12,6 +12,7 @@
12#include <linux/ptrace.h> 12#include <linux/ptrace.h>
13#include <asm/ptrace.h> 13#include <asm/ptrace.h>
14#undef __FRAME_OFFSETS 14#undef __FRAME_OFFSETS
15#include "user_constants.h"
15 16
16#define PT_INDEX(off) ((off) / sizeof(unsigned long)) 17#define PT_INDEX(off) ((off) / sizeof(unsigned long))
17 18
@@ -69,6 +70,8 @@
69#define REGS_IP_INDEX PT_INDEX(RIP) 70#define REGS_IP_INDEX PT_INDEX(RIP)
70#define REGS_SP_INDEX PT_INDEX(RSP) 71#define REGS_SP_INDEX PT_INDEX(RSP)
71 72
73#define FP_SIZE (HOST_FP_SIZE)
74
72#endif 75#endif
73 76
74/* 77/*