[PATCH] as-iosched: remove arq->is_sync member
[safe/jmp/linux-2.6] / kernel / signal.c
index 24d5059..fb5da6d 100644 (file)
@@ -10,7 +10,6 @@
  *             to allow signals to be sent reliably.
  */
 
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <linux/syscalls.h>
 #include <linux/ptrace.h>
 #include <linux/signal.h>
-#include <linux/audit.h>
 #include <linux/capability.h>
 #include <asm/param.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/siginfo.h>
+#include "audit.h"     /* audit_signal_info() */
 
 /*
  * SLAB caches for signal bits.
@@ -418,9 +417,8 @@ static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
 static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
                        siginfo_t *info)
 {
-       int sig = 0;
+       int sig = next_signal(pending, mask);
 
-       sig = next_signal(pending, mask);
        if (sig) {
                if (current->notifier) {
                        if (sigismember(current->notifier_mask, sig)) {
@@ -433,9 +431,7 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
 
                if (!collect_signal(sig, pending, info))
                        sig = 0;
-                               
        }
-       recalc_sigpending();
 
        return sig;
 }
@@ -452,6 +448,7 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
        if (!signr)
                signr = __dequeue_signal(&tsk->signal->shared_pending,
                                         mask, info);
+       recalc_sigpending_tsk(tsk);
        if (signr && unlikely(sig_kernel_stop(signr))) {
                /*
                 * Set a marker that we have dequeued a stop signal.  Our
@@ -584,16 +581,14 @@ static int check_kill_permission(int sig, struct siginfo *info,
            && !capable(CAP_KILL))
                return error;
 
-       error = security_task_kill(t, info, sig);
+       error = security_task_kill(t, info, sig, 0);
        if (!error)
                audit_signal_info(sig, t); /* Let audit system see the signal */
        return error;
 }
 
 /* forward decl */
-static void do_notify_parent_cldstop(struct task_struct *tsk,
-                                    int to_self,
-                                    int why);
+static void do_notify_parent_cldstop(struct task_struct *tsk, int why);
 
 /*
  * Handle magic process-wide effects of stop/continue signals.
@@ -643,7 +638,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
                        p->signal->group_stop_count = 0;
                        p->signal->flags = SIGNAL_STOP_CONTINUED;
                        spin_unlock(&p->sighand->siglock);
-                       do_notify_parent_cldstop(p, (p->ptrace & PT_PTRACED), CLD_STOPPED);
+                       do_notify_parent_cldstop(p, CLD_STOPPED);
                        spin_lock(&p->sighand->siglock);
                }
                rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending);
@@ -684,7 +679,7 @@ static void handle_stop_signal(int sig, struct task_struct *p)
                        p->signal->flags = SIGNAL_STOP_CONTINUED;
                        p->signal->group_exit_code = 0;
                        spin_unlock(&p->sighand->siglock);
-                       do_notify_parent_cldstop(p, (p->ptrace & PT_PTRACED), CLD_CONTINUED);
+                       do_notify_parent_cldstop(p, CLD_CONTINUED);
                        spin_lock(&p->sighand->siglock);
                } else {
                        /*
@@ -771,8 +766,7 @@ specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
        int ret = 0;
 
-       if (!irqs_disabled())
-               BUG();
+       BUG_ON(!irqs_disabled());
        assert_spin_locked(&t->sighand->siglock);
 
        /* Short-circuit ignored signals.  */
@@ -795,22 +789,31 @@ out:
 /*
  * Force a signal that the process can't ignore: if necessary
  * we unblock the signal and change any SIG_IGN to SIG_DFL.
+ *
+ * Note: If we unblock the signal, we always reset it to SIG_DFL,
+ * since we do not want to have a signal handler that was blocked
+ * be invoked when user space had explicitly blocked it.
+ *
+ * We don't want to have recursive SIGSEGV's etc, for example.
  */
-
 int
 force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
 {
        unsigned long int flags;
-       int ret;
+       int ret, blocked, ignored;
+       struct k_sigaction *action;
 
        spin_lock_irqsave(&t->sighand->siglock, flags);
-       if (t->sighand->action[sig-1].sa.sa_handler == SIG_IGN) {
-               t->sighand->action[sig-1].sa.sa_handler = SIG_DFL;
-       }
-       if (sigismember(&t->blocked, sig)) {
-               sigdelset(&t->blocked, sig);
+       action = &t->sighand->action[sig-1];
+       ignored = action->sa.sa_handler == SIG_IGN;
+       blocked = sigismember(&t->blocked, sig);
+       if (blocked || ignored) {
+               action->sa.sa_handler = SIG_DFL;
+               if (blocked) {
+                       sigdelset(&t->blocked, sig);
+                       recalc_sigpending_tsk(t);
+               }
        }
-       recalc_sigpending_tsk(t);
        ret = specific_send_sig_info(sig, info, t);
        spin_unlock_irqrestore(&t->sighand->siglock, flags);
 
@@ -871,7 +874,6 @@ __group_complete_signal(int sig, struct task_struct *p)
                if (t == NULL)
                        /* restart balancing at this thread */
                        t = p->signal->curr_target = p;
-               BUG_ON(t->tgid != p->tgid);
 
                while (!wants_signal(sig, t)) {
                        t = next_thread(t);
@@ -1111,7 +1113,7 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
 
 /* like kill_proc_info(), but doesn't use uid/euid of "current" */
 int kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid,
-                     uid_t uid, uid_t euid)
+                     uid_t uid, uid_t euid, u32 secid)
 {
        int ret = -EINVAL;
        struct task_struct *p;
@@ -1131,6 +1133,9 @@ int kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid,
                ret = -EPERM;
                goto out_unlock;
        }
+       ret = security_task_kill(p, info, sig, secid);
+       if (ret)
+               goto out_unlock;
        if (sig && p->sighand) {
                unsigned long flags;
                spin_lock_irqsave(&p->sighand->siglock, flags);
@@ -1311,12 +1316,10 @@ void sigqueue_free(struct sigqueue *q)
        __sigqueue_free(q);
 }
 
-int
-send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
+int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
 {
        unsigned long flags;
        int ret = 0;
-       struct sighand_struct *sh;
 
        BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
 
@@ -1330,48 +1333,17 @@ send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
         */
        rcu_read_lock();
 
-       if (unlikely(p->flags & PF_EXITING)) {
+       if (!likely(lock_task_sighand(p, &flags))) {
                ret = -1;
                goto out_err;
        }
 
-retry:
-       sh = rcu_dereference(p->sighand);
-
-       spin_lock_irqsave(&sh->siglock, flags);
-       if (p->sighand != sh) {
-               /* We raced with exec() in a multithreaded process... */
-               spin_unlock_irqrestore(&sh->siglock, flags);
-               goto retry;
-       }
-
-       /*
-        * We do the check here again to handle the following scenario:
-        *
-        * CPU 0                CPU 1
-        * send_sigqueue
-        * check PF_EXITING
-        * interrupt            exit code running
-        *                      __exit_signal
-        *                      lock sighand->siglock
-        *                      unlock sighand->siglock
-        * lock sh->siglock
-        * add(tsk->pending)    flush_sigqueue(tsk->pending)
-        *
-        */
-
-       if (unlikely(p->flags & PF_EXITING)) {
-               ret = -1;
-               goto out;
-       }
-
        if (unlikely(!list_empty(&q->list))) {
                /*
                 * If an SI_TIMER entry is already queue just increment
                 * the overrun count.
                 */
-               if (q->info.si_code != SI_TIMER)
-                       BUG();
+               BUG_ON(q->info.si_code != SI_TIMER);
                q->info.si_overrun++;
                goto out;
        }
@@ -1387,7 +1359,7 @@ retry:
                signal_wake_up(p, sig == SIGKILL);
 
 out:
-       spin_unlock_irqrestore(&sh->siglock, flags);
+       unlock_task_sighand(p, &flags);
 out_err:
        rcu_read_unlock();
 
@@ -1419,8 +1391,7 @@ send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
                 * the overrun count.  Other uses should not try to
                 * send the signal multiple times.
                 */
-               if (q->info.si_code != SI_TIMER)
-                       BUG();
+               BUG_ON(q->info.si_code != SI_TIMER);
                q->info.si_overrun++;
                goto out;
        } 
@@ -1519,14 +1490,14 @@ void do_notify_parent(struct task_struct *tsk, int sig)
        spin_unlock_irqrestore(&psig->siglock, flags);
 }
 
-static void do_notify_parent_cldstop(struct task_struct *tsk, int to_self, int why)
+static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
 {
        struct siginfo info;
        unsigned long flags;
        struct task_struct *parent;
        struct sighand_struct *sighand;
 
-       if (to_self)
+       if (tsk->ptrace & PT_PTRACED)
                parent = tsk->parent;
        else {
                tsk = tsk->group_leader;
@@ -1569,6 +1540,35 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int to_self, int w
        spin_unlock_irqrestore(&sighand->siglock, flags);
 }
 
+static inline int may_ptrace_stop(void)
+{
+       if (!likely(current->ptrace & PT_PTRACED))
+               return 0;
+
+       if (unlikely(current->parent == current->real_parent &&
+                   (current->ptrace & PT_ATTACHED)))
+               return 0;
+
+       if (unlikely(current->signal == current->parent->signal) &&
+           unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))
+               return 0;
+
+       /*
+        * Are we in the middle of do_coredump?
+        * If so and our tracer is also part of the coredump stopping
+        * is a deadlock situation, and pointless because our tracer
+        * is dead so don't allow us to stop.
+        * If SIGKILL was already sent before the caller unlocked
+        * ->siglock we must see ->core_waiters != 0. Otherwise it
+        * is safe to enter schedule().
+        */
+       if (unlikely(current->mm->core_waiters) &&
+           unlikely(current->mm == current->parent->mm))
+               return 0;
+
+       return 1;
+}
+
 /*
  * This must be called with current->sighand->siglock held.
  *
@@ -1595,13 +1595,10 @@ static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
        /* Let the debugger run.  */
        set_current_state(TASK_TRACED);
        spin_unlock_irq(&current->sighand->siglock);
+       try_to_freeze();
        read_lock(&tasklist_lock);
-       if (likely(current->ptrace & PT_PTRACED) &&
-           likely(current->parent != current->real_parent ||
-                  !(current->ptrace & PT_ATTACHED)) &&
-           (likely(current->parent->signal != current->signal) ||
-            !unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))) {
-               do_notify_parent_cldstop(current, 1, CLD_TRAPPED);
+       if (may_ptrace_stop()) {
+               do_notify_parent_cldstop(current, CLD_TRAPPED);
                read_unlock(&tasklist_lock);
                schedule();
        } else {
@@ -1650,25 +1647,17 @@ void ptrace_notify(int exit_code)
 static void
 finish_stop(int stop_count)
 {
-       int to_self;
-
        /*
         * If there are no other threads in the group, or if there is
         * a group stop in progress and we are the last to stop,
         * report to the parent.  When ptraced, every thread reports itself.
         */
-       if (current->ptrace & PT_PTRACED)
-               to_self = 1;
-       else if (stop_count == 0)
-               to_self = 0;
-       else
-               goto out;
-
-       read_lock(&tasklist_lock);
-       do_notify_parent_cldstop(current, to_self, CLD_STOPPED);
-       read_unlock(&tasklist_lock);
+       if (stop_count == 0 || (current->ptrace & PT_PTRACED)) {
+               read_lock(&tasklist_lock);
+               do_notify_parent_cldstop(current, CLD_STOPPED);
+               read_unlock(&tasklist_lock);
+       }
 
-out:
        schedule();
        /*
         * Now we don't run again until continued.
@@ -1799,9 +1788,9 @@ relock:
                        /* Let the debugger run.  */
                        ptrace_stop(signr, signr, info);
 
-                       /* We're back.  Did the debugger cancel the sig or group_exit? */
+                       /* We're back.  Did the debugger cancel the sig */
                        signr = current->exit_code;
-                       if (signr == 0 || current->signal->flags & SIGNAL_GROUP_EXIT)
+                       if (signr == 0)
                                continue;
 
                        current->exit_code = 0;
@@ -2586,6 +2575,11 @@ asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
 }
 #endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */
 
+__attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)
+{
+       return NULL;
+}
+
 void __init signals_init(void)
 {
        sigqueue_cachep =