fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / kernel / signal.c
index b25ab79..c0afecc 100644 (file)
 #include <linux/tracehook.h>
 #include <linux/signal.h>
 #include <linux/capability.h>
+#include <linux/freezer.h>
+#include <linux/pid_namespace.h>
+#include <linux/nsproxy.h>
+#include <linux/vs_context.h>
+#include <linux/vs_pid.h>
+
 #include <asm/param.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -33,7 +39,7 @@
  * SLAB caches for signal bits.
  */
 
-static kmem_cache_t *sigqueue_cachep;
+static struct kmem_cache *sigqueue_cachep;
 
 /*
  * In POSIX a signal is sent either to a specific thread (Linux task)
@@ -266,18 +272,25 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
                                         int override_rlimit)
 {
        struct sigqueue *q = NULL;
+       struct user_struct *user;
 
-       atomic_inc(&t->user->sigpending);
+       /*
+        * In order to avoid problems with "switch_user()", we want to make
+        * sure that the compiler doesn't re-load "t->user"
+        */
+       user = t->user;
+       barrier();
+       atomic_inc(&user->sigpending);
        if (override_rlimit ||
-           atomic_read(&t->user->sigpending) <=
+           atomic_read(&user->sigpending) <=
                        t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur)
                q = kmem_cache_alloc(sigqueue_cachep, flags);
        if (unlikely(q == NULL)) {
-               atomic_dec(&t->user->sigpending);
+               atomic_dec(&user->sigpending);
        } else {
                INIT_LIST_HEAD(&q->list);
                q->flags = 0;
-               q->user = get_uid(t->user);
+               q->user = get_uid(user);
        }
        return(q);
 }
@@ -335,7 +348,6 @@ flush_signal_handlers(struct task_struct *t, int force_default)
        }
 }
 
-EXPORT_SYMBOL_GPL(flush_signal_handlers);
 
 /* Notify the system that a driver wants to block all signals for this
  * process, and wants to be notified if any signals at all were to be
@@ -417,9 +429,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)) {
@@ -432,9 +443,7 @@ static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
 
                if (!collect_signal(sig, pending, info))
                        sig = 0;
-                               
        }
-       recalc_sigpending();
 
        return sig;
 }
@@ -451,6 +460,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
@@ -571,27 +581,35 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)
 static int check_kill_permission(int sig, struct siginfo *info,
                                 struct task_struct *t)
 {
-       int user;
        int error = -EINVAL;
 
        if (!valid_signal(sig))
                return error;
 
-       user = ((info == SEND_SIG_NOINFO) ||
-               (!is_si_special(info) && SI_FROMUSER(info)));
+       if ((info != SEND_SIG_NOINFO) &&
+               (is_si_special(info) || !SI_FROMUSER(info)))
+               goto skip;
+
+       vxdprintk(VXD_CBIT(misc, 7),
+               "check_kill_permission(%d,%p,%p[#%u,%u])",
+               sig, info, t, vx_task_xid(t), t->pid);
 
        error = -EPERM;
-       if (user && ((sig != SIGCONT) ||
-               (current->signal->session != t->signal->session))
+       if (((sig != SIGCONT) ||
+               (process_session(current) != process_session(t)))
            && (current->euid ^ t->suid) && (current->euid ^ t->uid)
            && (current->uid ^ t->suid) && (current->uid ^ t->uid)
            && !capable(CAP_KILL))
                return error;
 
        error = -ESRCH;
-       if (user && !vx_check(vx_task_xid(t), VX_ADMIN|VX_IDENT))
+       if (!vx_check(vx_task_xid(t), VS_WATCH_P|VS_IDENT)) {
+               vxdprintk(current->xid || VXD_CBIT(misc, 7),
+                       "signal %d[%p] xid mismatch %p[#%u,%u] xid=#%u",
+                       sig, info, t, vx_task_xid(t), t->pid, current->xid);
                return error;
-
+       }
+skip:
        error = security_task_kill(t, info, sig, 0);
        if (!error)
                audit_signal_info(sig, t); /* Let audit system see the signal */
@@ -1095,28 +1113,44 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
 }
 
 /*
- * kill_pg_info() sends a signal to a process group: this is what the tty
+ * kill_pgrp_info() sends a signal to a process group: this is what the tty
  * control characters do (^C, ^Z etc)
  */
 
-int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp)
+int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
 {
        struct task_struct *p = NULL;
        int retval, success;
 
-       if (pgrp <= 0)
-               return -EINVAL;
-
        success = 0;
        retval = -ESRCH;
-       do_each_task_pid(pgrp, PIDTYPE_PGID, p) {
+       do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
                int err = group_send_sig_info(sig, info, p);
                success |= !err;
                retval = err;
-       } while_each_task_pid(pgrp, PIDTYPE_PGID, p);
+       } while_each_pid_task(pgrp, PIDTYPE_PGID, p);
        return success ? 0 : retval;
 }
 
+int kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
+{
+       int retval;
+
+       read_lock(&tasklist_lock);
+       retval = __kill_pgrp_info(sig, info, pgrp);
+       read_unlock(&tasklist_lock);
+
+       return retval;
+}
+
+int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp)
+{
+       if (pgrp <= 0)
+               return -EINVAL;
+
+       return __kill_pgrp_info(sig, info, find_pid(pgrp));
+}
+
 int
 kill_pg_info(int sig, struct siginfo *info, pid_t pgrp)
 {
@@ -1129,8 +1163,7 @@ kill_pg_info(int sig, struct siginfo *info, pid_t pgrp)
        return retval;
 }
 
-int
-kill_proc_info(int sig, struct siginfo *info, pid_t pid)
+int kill_pid_info(int sig, struct siginfo *info, struct pid *pid)
 {
        int error;
        int acquired_tasklist_lock = 0;
@@ -1141,9 +1174,9 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
                read_lock(&tasklist_lock);
                acquired_tasklist_lock = 1;
        }
-       p = find_task_by_pid(pid);
+       p = pid_task(pid, PIDTYPE_PID);
        error = -ESRCH;
-       if (p && vx_check(vx_task_xid(p), VX_IDENT))
+       if (p && vx_check(vx_task_xid(p), VS_IDENT))
                error = group_send_sig_info(sig, info, p);
        if (unlikely(acquired_tasklist_lock))
                read_unlock(&tasklist_lock);
@@ -1151,8 +1184,17 @@ kill_proc_info(int sig, struct siginfo *info, pid_t pid)
        return error;
 }
 
-/* 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,
+static int kill_proc_info(int sig, struct siginfo *info, pid_t pid)
+{
+       int error;
+       rcu_read_lock();
+       error = kill_pid_info(sig, info, find_pid(vx_rmap_pid(pid)));
+       rcu_read_unlock();
+       return error;
+}
+
+/* like kill_pid_info(), but doesn't use uid/euid of "current" */
+int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
                      uid_t uid, uid_t euid, u32 secid)
 {
        int ret = -EINVAL;
@@ -1162,7 +1204,7 @@ int kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid,
                return ret;
 
        read_lock(&tasklist_lock);
-       p = find_task_by_pid(pid);
+       p = pid_task(pid, PIDTYPE_PID);
        if (!p) {
                ret = -ESRCH;
                goto out_unlock;
@@ -1186,7 +1228,7 @@ out_unlock:
        read_unlock(&tasklist_lock);
        return ret;
 }
-EXPORT_SYMBOL_GPL(kill_proc_info_as_uid);
+EXPORT_SYMBOL_GPL(kill_pid_info_as_uid);
 
 /*
  * kill_something_info() interprets pid in interesting ways just like kill(2).
@@ -1205,7 +1247,7 @@ static int kill_something_info(int sig, struct siginfo *info, int pid)
 
                read_lock(&tasklist_lock);
                for_each_process(p) {
-                       if (vx_check(vx_task_xid(p), VX_ADMIN|VX_IDENT) &&
+                       if (vx_check(vx_task_xid(p), VS_ADMIN_P|VS_IDENT) &&
                                p->pid > 1 && p->tgid != current->tgid) {
                                int err = group_send_sig_info(sig, info, p);
                                ++count;
@@ -1305,6 +1347,18 @@ force_sigsegv(int sig, struct task_struct *p)
        return 0;
 }
 
+int kill_pgrp(struct pid *pid, int sig, int priv)
+{
+       return kill_pgrp_info(sig, __si_special(priv), pid);
+}
+EXPORT_SYMBOL(kill_pgrp);
+
+int kill_pid(struct pid *pid, int sig, int priv)
+{
+       return kill_pid_info(sig, __si_special(priv), pid);
+}
+EXPORT_SYMBOL(kill_pid);
+
 int
 kill_pg(pid_t pgrp, int sig, int priv)
 {
@@ -1596,7 +1650,9 @@ finish_stop(int stop_count)
                read_unlock(&tasklist_lock);
        }
 
-       schedule();
+       do {
+               schedule();
+       } while (try_to_freeze());
        /*
         * Now we don't run again until continued.
         */
@@ -1751,8 +1807,12 @@ relock:
                if (sig_kernel_ignore(signr)) /* Default is nothing. */
                        continue;
 
-               /* Init gets no signals it doesn't want.  */
-               if (current == child_reaper)
+               /*
+                * Init of a pid space gets no signals it doesn't want from
+                * within that pid space. It can of course get signals from
+                * its parent pid space.
+                */
+               if (current == child_reaper(current))
                        continue;
 
                /* virtual init is protected against user signals */
@@ -2499,6 +2559,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 =