3.16.7-ckt2 -stable review patch.  If anyone has any objections, please let me 
know.

------------------

From: Oleg Nesterov <o...@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 <b...@azulsystems.com>
Signed-off-by: Oleg Nesterov <o...@redhat.com>
Link: http://lkml.kernel.org/r/20140902175713.ga21...@redhat.com
Signed-off-by: H. Peter Anvin <h...@linux.intel.com>
Signed-off-by: Luis Henriques <luis.henriq...@canonical.com>
---
 arch/x86/kernel/signal.c | 5 +++++
 arch/x86/kernel/xsave.c  | 2 --
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 2851d63c1202..ed37a768d0fc 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -675,6 +675,11 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
                 * 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));
 }
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c
index 4679ef4005da..dd50e26c58f6 100644
--- a/arch/x86/kernel/xsave.c
+++ b/arch/x86/kernel/xsave.c
@@ -268,8 +268,6 @@ int save_xstate_sig(void __user *buf, void __user *buf_fx, 
int size)
        if (use_fxsr() && save_xstate_epilog(buf_fx, ia32_fxstate))
                return -1;
 
-       drop_init_fpu(tsk);     /* trigger finit */
-
        return 0;
 }
 
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to