x86: signal: check signal stack overflow properly
authorHiroshi Shimamoto <h-shimamoto@ct.jp.nec.com>
Thu, 19 Mar 2009 17:56:29 +0000 (10:56 -0700)
committerIngo Molnar <mingo@elte.hu>
Fri, 20 Mar 2009 18:01:31 +0000 (19:01 +0100)
Impact: cleanup

Check alternate signal stack overflow with proper stack pointer.
The stack pointer of the next signal frame is different if that
task has i387 state.

On x86_64, redzone would be included.

No need to check SA_ONSTACK if we're already using alternate signal stack.

Signed-off-by: Hiroshi Shimamoto <h-shimamoto@ct.jp.nec.com>
Cc: Roland McGrath <roland@redhat.com>
LKML-Reference: <49C2874D.3080002@ct.jp.nec.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/signal.c

index d2cc642..dfcc74a 100644 (file)
@@ -211,31 +211,27 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
 {
        /* Default to using normal stack */
        unsigned long sp = regs->sp;
+       int onsigstack = on_sig_stack(sp);
 
 #ifdef CONFIG_X86_64
        /* redzone */
        sp -= 128;
 #endif /* CONFIG_X86_64 */
 
-       /*
-        * If we are on the alternate signal stack and would overflow it, don't.
-        * Return an always-bogus address instead so we will die with SIGSEGV.
-        */
-       if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size)))
-               return (void __user *) -1L;
-
-       /* This is the X/Open sanctioned signal stack switching.  */
-       if (ka->sa.sa_flags & SA_ONSTACK) {
-               if (sas_ss_flags(sp) == 0)
-                       sp = current->sas_ss_sp + current->sas_ss_size;
-       } else {
+       if (!onsigstack) {
+               /* This is the X/Open sanctioned signal stack switching.  */
+               if (ka->sa.sa_flags & SA_ONSTACK) {
+                       if (sas_ss_flags(sp) == 0)
+                               sp = current->sas_ss_sp + current->sas_ss_size;
+               } else {
 #ifdef CONFIG_X86_32
-               /* This is the legacy signal stack switching. */
-               if ((regs->ss & 0xffff) != __USER_DS &&
-                       !(ka->sa.sa_flags & SA_RESTORER) &&
-                               ka->sa.sa_restorer)
-                       sp = (unsigned long) ka->sa.sa_restorer;
+                       /* This is the legacy signal stack switching. */
+                       if ((regs->ss & 0xffff) != __USER_DS &&
+                               !(ka->sa.sa_flags & SA_RESTORER) &&
+                                       ka->sa.sa_restorer)
+                               sp = (unsigned long) ka->sa.sa_restorer;
 #endif /* CONFIG_X86_32 */
+               }
        }
 
        if (used_math()) {
@@ -244,12 +240,22 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
                sp = round_down(sp, 64);
 #endif /* CONFIG_X86_64 */
                *fpstate = (void __user *)sp;
-
-               if (save_i387_xstate(*fpstate) < 0)
-                       return (void __user *)-1L;
        }
 
-       return (void __user *)align_sigframe(sp - frame_size);
+       sp = align_sigframe(sp - frame_size);
+
+       /*
+        * If we are on the alternate signal stack and would overflow it, don't.
+        * Return an always-bogus address instead so we will die with SIGSEGV.
+        */
+       if (onsigstack && !likely(on_sig_stack(sp)))
+               return (void __user *)-1L;
+
+       /* save i387 state */
+       if (used_math() && save_i387_xstate(*fpstate) < 0)
+               return (void __user *)-1L;
+
+       return (void __user *)sp;
 }
 
 #ifdef CONFIG_X86_32