| From 66463db4fc5605d51c7bb81d009d5bf30a783a2c Mon Sep 17 00:00:00 2001 |
| From: Oleg Nesterov <oleg@redhat.com> |
| Date: Tue, 2 Sep 2014 19:57:13 +0200 |
| Subject: x86, fpu: shift drop_init_fpu() from save_xstate_sig() to handle_signal() |
| |
| From: Oleg Nesterov <oleg@redhat.com> |
| |
| commit 66463db4fc5605d51c7bb81d009d5bf30a783a2c upstream. |
| |
| save_xstate_sig()->drop_init_fpu() doesn't look right. setup_rt_frame() |
| can fail after that, in this case the next setup_rt_frame() triggered |
| by SIGSEGV won't save fpu simply because the old state was lost. This |
| obviously mean that fpu won't be restored after sys_rt_sigreturn() from |
| SIGSEGV handler. |
| |
| Shift drop_init_fpu() into !failed branch in handle_signal(). |
| |
| Test-case (needs -O2): |
| |
| #include <stdio.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <sys/syscall.h> |
| #include <sys/mman.h> |
| #include <pthread.h> |
| #include <assert.h> |
| |
| volatile double D; |
| |
| void test(double d) |
| { |
| int pid = getpid(); |
| |
| for (D = d; D == d; ) { |
| /* sys_tkill(pid, SIGHUP); asm to avoid save/reload |
| * fp regs around "C" call */ |
| asm ("" : : "a"(200), "D"(pid), "S"(1)); |
| asm ("syscall" : : : "ax"); |
| } |
| |
| printf("ERR!!\n"); |
| } |
| |
| void sigh(int sig) |
| { |
| } |
| |
| char altstack[4096 * 10] __attribute__((aligned(4096))); |
| |
| void *tfunc(void *arg) |
| { |
| for (;;) { |
| mprotect(altstack, sizeof(altstack), PROT_READ); |
| mprotect(altstack, sizeof(altstack), PROT_READ|PROT_WRITE); |
| } |
| } |
| |
| int main(void) |
| { |
| stack_t st = { |
| .ss_sp = altstack, |
| .ss_size = sizeof(altstack), |
| .ss_flags = SS_ONSTACK, |
| }; |
| |
| struct sigaction sa = { |
| .sa_handler = sigh, |
| }; |
| |
| pthread_t pt; |
| |
| sigaction(SIGSEGV, &sa, NULL); |
| sigaltstack(&st, NULL); |
| sa.sa_flags = SA_ONSTACK; |
| sigaction(SIGHUP, &sa, NULL); |
| |
| pthread_create(&pt, NULL, tfunc, NULL); |
| |
| test(123.456); |
| return 0; |
| } |
| |
| Reported-by: Bean Anderson <bean@azulsystems.com> |
| Signed-off-by: Oleg Nesterov <oleg@redhat.com> |
| Link: http://lkml.kernel.org/r/20140902175713.GA21646@redhat.com |
| Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/signal.c | 5 +++++ |
| arch/x86/kernel/xsave.c | 2 -- |
| 2 files changed, 5 insertions(+), 2 deletions(-) |
| |
| --- a/arch/x86/kernel/signal.c |
| +++ b/arch/x86/kernel/signal.c |
| @@ -675,6 +675,11 @@ handle_signal(struct ksignal *ksig, stru |
| * handler too. |
| */ |
| regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF); |
| + /* |
| + * Ensure the signal handler starts with the new fpu state. |
| + */ |
| + if (used_math()) |
| + drop_init_fpu(current); |
| } |
| signal_setup_done(failed, ksig, test_thread_flag(TIF_SINGLESTEP)); |
| } |
| --- a/arch/x86/kernel/xsave.c |
| +++ b/arch/x86/kernel/xsave.c |
| @@ -271,8 +271,6 @@ int save_xstate_sig(void __user *buf, vo |
| if (use_fxsr() && save_xstate_epilog(buf_fx, ia32_fxstate)) |
| return -1; |
| |
| - drop_init_fpu(tsk); /* trigger finit */ |
| - |
| return 0; |
| } |
| |