[PATCH] i386: never block forced SIGSEGV
authorRoland McGrath <roland@redhat.com>
Thu, 23 Jun 2005 07:08:21 +0000 (00:08 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 23 Jun 2005 16:45:09 +0000 (09:45 -0700)
This problem was first noticed on PPC and has already been fixed there.
But the exact same issue applies to other platforms in the same way.  The
signal blocking for sa_mask and the handled signal takes place after the
handler setup.  When the stack is bogus, the handler setup forces a
SIGSEGV.  But then this will be blocked, and returning to user mode will
fault again and iterate.  This patch fixes the problem by checking whether
signal handler setup failed, and not doing the signal-blocking if so.  This
copies what was done in the ppc code.  I think all architectures' signal
handler setup code follows this pattern and needs the change.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/kernel/signal.c

index ea46d028af0872f5df65188503fcd9f71f3004e1..344400787c399a1a04ccc232c9a8d308ea9080d5 100644 (file)
@@ -346,8 +346,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
 extern void __user __kernel_sigreturn;
 extern void __user __kernel_rt_sigreturn;
 
 extern void __user __kernel_sigreturn;
 extern void __user __kernel_rt_sigreturn;
 
-static void setup_frame(int sig, struct k_sigaction *ka,
-                       sigset_t *set, struct pt_regs * regs)
+static int setup_frame(int sig, struct k_sigaction *ka,
+                      sigset_t *set, struct pt_regs * regs)
 {
        void __user *restorer;
        struct sigframe __user *frame;
 {
        void __user *restorer;
        struct sigframe __user *frame;
@@ -429,13 +429,14 @@ static void setup_frame(int sig, struct k_sigaction *ka,
                current->comm, current->pid, frame, regs->eip, frame->pretcode);
 #endif
 
                current->comm, current->pid, frame, regs->eip, frame->pretcode);
 #endif
 
-       return;
+       return 1;
 
 give_sigsegv:
        force_sigsegv(sig, current);
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return 0;
 }
 
 }
 
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                           sigset_t *set, struct pt_regs * regs)
 {
        void __user *restorer;
                           sigset_t *set, struct pt_regs * regs)
 {
        void __user *restorer;
@@ -522,20 +523,23 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                current->comm, current->pid, frame, regs->eip, frame->pretcode);
 #endif
 
                current->comm, current->pid, frame, regs->eip, frame->pretcode);
 #endif
 
-       return;
+       return 1;
 
 give_sigsegv:
        force_sigsegv(sig, current);
 
 give_sigsegv:
        force_sigsegv(sig, current);
+       return 0;
 }
 
 /*
  * OK, we're invoking a handler
  */    
 
 }
 
 /*
  * OK, we're invoking a handler
  */    
 
-static void
+static int
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
              sigset_t *oldset, struct pt_regs * regs)
 {
 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
              sigset_t *oldset, struct pt_regs * regs)
 {
+       int ret;
+
        /* Are we from a system call? */
        if (regs->orig_eax >= 0) {
                /* If so, check system call restarting.. */
        /* Are we from a system call? */
        if (regs->orig_eax >= 0) {
                /* If so, check system call restarting.. */
@@ -569,17 +573,19 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
 
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
 
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
-               setup_rt_frame(sig, ka, info, oldset, regs);
+               ret = setup_rt_frame(sig, ka, info, oldset, regs);
        else
        else
-               setup_frame(sig, ka, oldset, regs);
+               ret = setup_frame(sig, ka, oldset, regs);
 
 
-       if (!(ka->sa.sa_flags & SA_NODEFER)) {
+       if (ret && !(ka->sa.sa_flags & SA_NODEFER)) {
                spin_lock_irq(&current->sighand->siglock);
                sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
                sigaddset(&current->blocked,sig);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
        }
                spin_lock_irq(&current->sighand->siglock);
                sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
                sigaddset(&current->blocked,sig);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
        }
+
+       return ret;
 }
 
 /*
 }
 
 /*
@@ -622,8 +628,7 @@ int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset)
                }
 
                /* Whee!  Actually deliver the signal.  */
                }
 
                /* Whee!  Actually deliver the signal.  */
-               handle_signal(signr, &info, &ka, oldset, regs);
-               return 1;
+               return handle_signal(signr, &info, &ka, oldset, regs);
        }
 
  no_signal:
        }
 
  no_signal: