#include <linux/ptrace.h>
#include <linux/posix-timers.h>
#include <linux/signal.h>
+#include <linux/audit.h>
+#include <linux/capability.h>
+#include <linux/vs_cvirt.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
fastcall void recalc_sigpending_tsk(struct task_struct *t)
{
if (t->signal->group_stop_count > 0 ||
+ (freezing(t)) ||
PENDING(&t->pending, &t->blocked) ||
PENDING(&t->signal->shared_pending, &t->blocked))
set_tsk_thread_flag(t, TIF_SIGPENDING);
return sig;
}
-static struct sigqueue *__sigqueue_alloc(struct task_struct *t, unsigned int __nocast flags,
+static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
int override_rlimit)
{
struct sigqueue *q = NULL;
} else {
INIT_LIST_HEAD(&q->list);
q->flags = 0;
- q->lock = NULL;
q->user = get_uid(t->user);
}
return(q);
}
-static inline void __sigqueue_free(struct sigqueue *q)
+static void __sigqueue_free(struct sigqueue *q)
{
if (q->flags & SIGQUEUE_PREALLOC)
return;
/* Ok, we're done with the signal handlers */
tsk->sighand = NULL;
if (atomic_dec_and_test(&sighand->count))
- kmem_cache_free(sighand_cachep, sighand);
+ sighand_free(sighand);
}
void exit_sighand(struct task_struct *tsk)
{
write_lock_irq(&tasklist_lock);
- __exit_sighand(tsk);
+ rcu_read_lock();
+ if (tsk->sighand != NULL) {
+ struct sighand_struct *sighand = rcu_dereference(tsk->sighand);
+ spin_lock(&sighand->siglock);
+ __exit_sighand(tsk);
+ spin_unlock(&sighand->siglock);
+ }
+ rcu_read_unlock();
write_unlock_irq(&tasklist_lock);
}
void __exit_signal(struct task_struct *tsk)
{
struct signal_struct * sig = tsk->signal;
- struct sighand_struct * sighand = tsk->sighand;
+ struct sighand_struct * sighand;
if (!sig)
BUG();
if (!atomic_read(&sig->count))
BUG();
+ rcu_read_lock();
+ sighand = rcu_dereference(tsk->sighand);
spin_lock(&sighand->siglock);
posix_cpu_timers_exit(tsk);
if (atomic_dec_and_test(&sig->count)) {
posix_cpu_timers_exit_group(tsk);
- if (tsk == sig->curr_target)
- sig->curr_target = next_thread(tsk);
tsk->signal = NULL;
+ __exit_sighand(tsk);
spin_unlock(&sighand->siglock);
flush_sigqueue(&sig->shared_pending);
} else {
sig->nvcsw += tsk->nvcsw;
sig->nivcsw += tsk->nivcsw;
sig->sched_time += tsk->sched_time;
+ __exit_sighand(tsk);
spin_unlock(&sighand->siglock);
sig = NULL; /* Marker for below. */
}
+ rcu_read_unlock();
clear_tsk_thread_flag(tsk,TIF_SIGPENDING);
flush_sigqueue(&tsk->pending);
if (sig) {
/*
- * We are cleaning up the signal_struct here. We delayed
- * calling exit_itimers until after flush_sigqueue, just in
- * case our thread-local pending queue contained a queued
- * timer signal that would have been cleared in
- * exit_itimers. When that called sigqueue_free, it would
- * attempt to re-take the tasklist_lock and deadlock. This
- * can never happen if we ensure that all queues the
- * timer's signal might be queued on have been flushed
- * first. The shared_pending queue, and our own pending
- * queue are the only queues the timer could be on, since
- * there are no other threads left in the group and timer
- * signals are constrained to threads inside the group.
+ * We are cleaning up the signal_struct here.
*/
- exit_itimers(sig);
exit_thread_group_keys(sig);
kmem_cache_free(signal_cachep, sig);
}
void exit_signal(struct task_struct *tsk)
{
+ atomic_dec(&tsk->signal->live);
+
write_lock_irq(&tasklist_lock);
__exit_signal(tsk);
write_unlock_irq(&tasklist_lock);
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
}
-static inline int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
+static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
{
struct sigqueue *q, *first = NULL;
int still_pending = 0;
{
int sig = 0;
- /* SIGKILL must have priority, otherwise it is quite easy
- * to create an unkillable process, sending sig < SIGKILL
- * to self */
- if (unlikely(sigismember(&pending->signal, SIGKILL))) {
- if (!sigismember(mask, SIGKILL))
- sig = SIGKILL;
- }
-
- if (likely(!sig))
- sig = next_signal(pending, mask);
+ sig = next_signal(pending, mask);
if (sig) {
if (current->notifier) {
if (sigismember(current->notifier_mask, sig)) {
* is to alert stop-signal processing code when another
* processor has come along and cleared the flag.
*/
- tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
+ if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
+ tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
}
if ( signr &&
((info->si_code & __SI_MASK) == __SI_TIMER) &&
kick_process(t);
}
+/*
+ * Remove signals in mask from the pending set and queue.
+ * Returns 1 if any signals were found.
+ *
+ * All callers must be holding the siglock.
+ *
+ * This version takes a sigset mask and looks at all signals,
+ * not just those in the first mask word.
+ */
+static int rm_from_queue_full(sigset_t *mask, struct sigpending *s)
+{
+ struct sigqueue *q, *n;
+ sigset_t m;
+
+ sigandsets(&m, mask, &s->signal);
+ if (sigisemptyset(&m))
+ return 0;
+
+ signandsets(&s->signal, &s->signal, mask);
+ list_for_each_entry_safe(q, n, &s->list, list) {
+ if (sigismember(mask, q->info.si_signo)) {
+ list_del_init(&q->list);
+ __sigqueue_free(q);
+ }
+ }
+ return 1;
+}
/*
* Remove signals in mask from the pending set and queue.
* Returns 1 if any signals were found.
static int check_kill_permission(int sig, struct siginfo *info,
struct task_struct *t)
{
- int error = -EINVAL;
int user;
+ int error = -EINVAL;
if (!valid_signal(sig))
return error;
- user = (!info || ((unsigned long)info != 1 &&
- (unsigned long)info != 2 && SI_FROMUSER(info)));
+ user = ((info == SEND_SIG_NOINFO) ||
+ (!is_si_special(info) && SI_FROMUSER(info)));
error = -EPERM;
if (user && ((sig != SIGCONT) ||
if (user && !vx_check(vx_task_xid(t), VX_ADMIN|VX_IDENT))
return error;
- return security_task_kill(t, info, sig);
+ error = security_task_kill(t, info, sig);
+ 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,
- struct task_struct *parent,
+ int to_self,
int why);
/*
{
struct task_struct *t;
- if (p->flags & SIGNAL_GROUP_EXIT)
+ if (p->signal->flags & SIGNAL_GROUP_EXIT)
/*
* The process is in the middle of dying already.
*/
p->signal->group_stop_count = 0;
p->signal->flags = SIGNAL_STOP_CONTINUED;
spin_unlock(&p->sighand->siglock);
- if (p->ptrace & PT_PTRACED)
- do_notify_parent_cldstop(p, p->parent,
- CLD_STOPPED);
- else
- do_notify_parent_cldstop(
- p->group_leader,
- p->group_leader->real_parent,
- CLD_STOPPED);
+ do_notify_parent_cldstop(p, (p->ptrace & PT_PTRACED), CLD_STOPPED);
spin_lock(&p->sighand->siglock);
}
rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending);
p->signal->flags = SIGNAL_STOP_CONTINUED;
p->signal->group_exit_code = 0;
spin_unlock(&p->sighand->siglock);
- if (p->ptrace & PT_PTRACED)
- do_notify_parent_cldstop(p, p->parent,
- CLD_CONTINUED);
- else
- do_notify_parent_cldstop(
- p->group_leader,
- p->group_leader->real_parent,
- CLD_CONTINUED);
+ do_notify_parent_cldstop(p, (p->ptrace & PT_PTRACED), CLD_CONTINUED);
spin_lock(&p->sighand->siglock);
} else {
/*
* fast-pathed signals for kernel-internal things like SIGSTOP
* or SIGKILL.
*/
- if ((unsigned long)info == 2)
+ if (info == SEND_SIG_FORCED)
goto out_set;
/* Real-time signals must be queued if sent by sigqueue, or
pass on the info struct. */
q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN &&
- ((unsigned long) info < 2 ||
+ (is_si_special(info) ||
info->si_code >= 0)));
if (q) {
list_add_tail(&q->list, &signals->list);
switch ((unsigned long) info) {
- case 0:
+ case (unsigned long) SEND_SIG_NOINFO:
q->info.si_signo = sig;
q->info.si_errno = 0;
q->info.si_code = SI_USER;
q->info.si_pid = current->pid;
q->info.si_uid = current->uid;
break;
- case 1:
+ case (unsigned long) SEND_SIG_PRIV:
q->info.si_signo = sig;
q->info.si_errno = 0;
q->info.si_code = SI_KERNEL;
copy_siginfo(&q->info, info);
break;
}
- } else {
- if (sig >= SIGRTMIN && info && (unsigned long)info != 1
- && info->si_code != SI_USER)
+ } else if (!is_si_special(info)) {
+ if (sig >= SIGRTMIN && info->si_code != SI_USER)
/*
* Queue overflow, abort. We may abort if the signal was rt
* and sent by user using something other than kill().
*/
return -EAGAIN;
- if (((unsigned long)info > 1) && (info->si_code == SI_TIMER))
- /*
- * Set up a return to indicate that we dropped
- * the signal.
- */
- ret = info->si_sys_private;
}
out_set:
BUG();
assert_spin_locked(&t->sighand->siglock);
- if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))
- /*
- * Set up a return to indicate that we dropped the signal.
- */
- ret = info->si_sys_private;
-
/* Short-circuit ignored signals. */
if (sig_ignored(t, sig))
goto out;
int ret;
spin_lock_irqsave(&t->sighand->siglock, flags);
- if (sigismember(&t->blocked, sig) || t->sighand->action[sig-1].sa.sa_handler == SIG_IGN) {
+ 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);
- recalc_sigpending_tsk(t);
}
+ recalc_sigpending_tsk(t);
ret = specific_send_sig_info(sig, info, t);
spin_unlock_irqrestore(&t->sighand->siglock, flags);
void
force_sig_specific(int sig, struct task_struct *t)
{
- unsigned long int flags;
-
- 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;
- sigdelset(&t->blocked, sig);
- recalc_sigpending_tsk(t);
- specific_send_sig_info(sig, (void *)2, t);
- spin_unlock_irqrestore(&t->sighand->siglock, flags);
+ force_sig_info(sig, SEND_SIG_FORCED, t);
}
/*
* as soon as they're available, so putting the signal on the shared queue
* will be equivalent to sending it to one such thread.
*/
-#define wants_signal(sig, p, mask) \
- (!sigismember(&(p)->blocked, sig) \
- && !((p)->state & mask) \
- && !((p)->flags & PF_EXITING) \
- && (task_curr(p) || !signal_pending(p)))
-
+static inline int wants_signal(int sig, struct task_struct *p)
+{
+ if (sigismember(&p->blocked, sig))
+ return 0;
+ if (p->flags & PF_EXITING)
+ return 0;
+ if (sig == SIGKILL)
+ return 1;
+ if (p->state & (TASK_STOPPED | TASK_TRACED))
+ return 0;
+ return task_curr(p) || !signal_pending(p);
+}
static void
__group_complete_signal(int sig, struct task_struct *p)
{
- unsigned int mask;
struct task_struct *t;
- /*
- * Don't bother traced and stopped tasks (but
- * SIGKILL will punch through that).
- */
- mask = TASK_STOPPED | TASK_TRACED;
- if (sig == SIGKILL)
- mask = 0;
-
/*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
- if (wants_signal(sig, p, mask))
+ if (wants_signal(sig, p))
t = p;
else if (thread_group_empty(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, mask)) {
+ while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == p->signal->curr_target)
/*
assert_spin_locked(&p->sighand->siglock);
handle_stop_signal(sig, p);
- if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))
- /*
- * Set up a return to indicate that we dropped the signal.
- */
- ret = info->si_sys_private;
-
/* Short-circuit ignored signals. */
if (sig_ignored(p, sig))
return ret;
if (t != p->group_leader)
t->exit_signal = -1;
+ /* SIGKILL will be handled before any pending SIGSTOP */
sigaddset(&t->pending.signal, SIGKILL);
- rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
signal_wake_up(t, 1);
}
}
/*
- * Must be called with the tasklist_lock held for reading!
+ * Must be called under rcu_read_lock() or with tasklist_lock read-held.
*/
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
unsigned long flags;
+ struct sighand_struct *sp;
int ret;
+retry:
ret = check_kill_permission(sig, info, p);
- if (!ret && sig && p->sighand) {
- spin_lock_irqsave(&p->sighand->siglock, flags);
+ if (!ret && sig && (sp = rcu_dereference(p->sighand))) {
+ spin_lock_irqsave(&sp->siglock, flags);
+ if (p->sighand != sp) {
+ spin_unlock_irqrestore(&sp->siglock, flags);
+ goto retry;
+ }
+ if ((atomic_read(&sp->count) == 0) ||
+ (atomic_read(&p->usage) == 0)) {
+ spin_unlock_irqrestore(&sp->siglock, flags);
+ return -ESRCH;
+ }
ret = __group_send_sig_info(sig, info, p);
- spin_unlock_irqrestore(&p->sighand->siglock, flags);
+ spin_unlock_irqrestore(&sp->siglock, flags);
}
return ret;
kill_proc_info(int sig, struct siginfo *info, pid_t pid)
{
int error;
+ int acquired_tasklist_lock = 0;
struct task_struct *p;
- read_lock(&tasklist_lock);
+ rcu_read_lock();
+ if (unlikely(sig_kernel_stop(sig) || sig == SIGCONT)) {
+ read_lock(&tasklist_lock);
+ acquired_tasklist_lock = 1;
+ }
p = find_task_by_pid(pid);
error = -ESRCH;
- if (p)
+ if (p && vx_check(vx_task_xid(p), VX_IDENT))
error = group_send_sig_info(sig, info, p);
- read_unlock(&tasklist_lock);
+ if (unlikely(acquired_tasklist_lock))
+ read_unlock(&tasklist_lock);
+ rcu_read_unlock();
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,
+ uid_t uid, uid_t euid)
+{
+ int ret = -EINVAL;
+ struct task_struct *p;
+
+ if (!valid_signal(sig))
+ return ret;
+
+ read_lock(&tasklist_lock);
+ p = find_task_by_pid(pid);
+ if (!p) {
+ ret = -ESRCH;
+ goto out_unlock;
+ }
+ if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
+ && (euid != p->suid) && (euid != p->uid)
+ && (uid != p->suid) && (uid != p->uid)) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+ if (sig && p->sighand) {
+ unsigned long flags;
+ spin_lock_irqsave(&p->sighand->siglock, flags);
+ ret = __group_send_sig_info(sig, info, p);
+ spin_unlock_irqrestore(&p->sighand->siglock, flags);
+ }
+out_unlock:
+ read_unlock(&tasklist_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kill_proc_info_as_uid);
/*
* kill_something_info() interprets pid in interesting ways just like kill(2).
read_lock(&tasklist_lock);
for_each_process(p) {
- if (p->pid > 1 && p->tgid != current->tgid) {
+ if (vx_check(vx_task_xid(p), VX_ADMIN|VX_IDENT)&&
+ p->pid > 1 && p->tgid != current->tgid) {
int err = group_send_sig_info(sig, info, p);
++count;
if (err != -EPERM)
return ret;
}
+#define __si_special(priv) \
+ ((priv) ? SEND_SIG_PRIV : SEND_SIG_NOINFO)
+
int
send_sig(int sig, struct task_struct *p, int priv)
{
- return send_sig_info(sig, (void*)(long)(priv != 0), p);
+ return send_sig_info(sig, __si_special(priv), p);
}
/*
void
force_sig(int sig, struct task_struct *p)
{
- force_sig_info(sig, (void*)1L, p);
+ force_sig_info(sig, SEND_SIG_PRIV, p);
}
/*
int
kill_pg(pid_t pgrp, int sig, int priv)
{
- return kill_pg_info(sig, (void *)(long)(priv != 0), pgrp);
+ return kill_pg_info(sig, __si_special(priv), pgrp);
}
int
kill_proc(pid_t pid, int sig, int priv)
{
- return kill_proc_info(sig, (void *)(long)(priv != 0), pid);
+ return kill_proc_info(sig, __si_special(priv), pid);
}
/*
* pending queue.
*/
if (unlikely(!list_empty(&q->list))) {
- read_lock(&tasklist_lock);
- spin_lock_irqsave(q->lock, flags);
+ spinlock_t *lock = ¤t->sighand->siglock;
+ read_lock(&tasklist_lock);
+ spin_lock_irqsave(lock, flags);
if (!list_empty(&q->list))
list_del_init(&q->list);
- spin_unlock_irqrestore(q->lock, flags);
+ spin_unlock_irqrestore(lock, flags);
read_unlock(&tasklist_lock);
}
q->flags &= ~SIGQUEUE_PREALLOC;
{
unsigned long flags;
int ret = 0;
+ struct sighand_struct *sh;
+
+ BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
/*
- * We need the tasklist lock even for the specific
- * thread case (when we don't need to follow the group
- * lists) in order to avoid races with "p->sighand"
- * going away or changing from under us.
+ * The rcu based delayed sighand destroy makes it possible to
+ * run this without tasklist lock held. The task struct itself
+ * cannot go away as create_timer did get_task_struct().
+ *
+ * We return -1, when the task is marked exiting, so
+ * posix_timer_event can redirect it to the group leader
*/
- BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
- read_lock(&tasklist_lock);
- spin_lock_irqsave(&p->sighand->siglock, flags);
-
+ rcu_read_lock();
+
+ if (unlikely(p->flags & PF_EXITING)) {
+ 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
BUG();
q->info.si_overrun++;
goto out;
- }
+ }
/* Short-circuit ignored signals. */
if (sig_ignored(p, sig)) {
ret = 1;
goto out;
}
- q->lock = &p->sighand->siglock;
list_add_tail(&q->list, &p->pending.list);
sigaddset(&p->pending.signal, sig);
if (!sigismember(&p->blocked, sig))
signal_wake_up(p, sig == SIGKILL);
out:
- spin_unlock_irqrestore(&p->sighand->siglock, flags);
- read_unlock(&tasklist_lock);
- return(ret);
+ spin_unlock_irqrestore(&sh->siglock, flags);
+out_err:
+ rcu_read_unlock();
+
+ return ret;
}
int
int ret = 0;
BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
+
read_lock(&tasklist_lock);
+ /* Since it_lock is held, p->sighand cannot be NULL. */
spin_lock_irqsave(&p->sighand->siglock, flags);
handle_stop_signal(sig, p);
* We always use the shared queue for process-wide signals,
* to avoid several races.
*/
- q->lock = &p->sighand->siglock;
list_add_tail(&q->list, &p->signal->shared_pending.list);
sigaddset(&p->signal->shared_pending.signal, sig);
out:
spin_unlock_irqrestore(&p->sighand->siglock, flags);
read_unlock(&tasklist_lock);
- return(ret);
+ return ret;
}
/*
psig = tsk->parent->sighand;
spin_lock_irqsave(&psig->siglock, flags);
- if (sig == SIGCHLD &&
+ if (!tsk->ptrace && sig == SIGCHLD &&
(psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
(psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {
/*
spin_unlock_irqrestore(&psig->siglock, flags);
}
-static void
-do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent,
- int why)
+static void do_notify_parent_cldstop(struct task_struct *tsk, int to_self, int why)
{
struct siginfo info;
unsigned long flags;
+ struct task_struct *parent;
struct sighand_struct *sighand;
+ if (to_self)
+ parent = tsk->parent;
+ else {
+ tsk = tsk->group_leader;
+ parent = tsk->real_parent;
+ }
+
info.si_signo = SIGCHLD;
info.si_errno = 0;
info.si_pid = tsk->pid;
/* Let the debugger run. */
set_current_state(TASK_TRACED);
spin_unlock_irq(¤t->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, current->parent,
- CLD_TRAPPED);
+ do_notify_parent_cldstop(current, 1, CLD_TRAPPED);
read_unlock(&tasklist_lock);
schedule();
} else {
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 (stop_count < 0 || (current->ptrace & PT_PTRACED)) {
- read_lock(&tasklist_lock);
- do_notify_parent_cldstop(current, current->parent,
- CLD_STOPPED);
- read_unlock(&tasklist_lock);
- }
- else if (stop_count == 0) {
- read_lock(&tasklist_lock);
- do_notify_parent_cldstop(current->group_leader,
- current->group_leader->real_parent,
- CLD_STOPPED);
- read_unlock(&tasklist_lock);
- }
+ if (stop_count < 0 || (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);
+
+out:
schedule();
/*
* Now we don't run again until continued.
* stop is always done with the siglock held,
* so this check has no races.
*/
- if (t->state < TASK_STOPPED) {
+ if (!t->exit_state &&
+ !(t->state & (TASK_STOPPED|TASK_TRACED))) {
stop_count++;
signal_wake_up(t, 0);
}
* We return zero if we still hold the siglock and should look
* for another signal without checking group_stop_count again.
*/
-static inline int handle_group_stop(void)
+static int handle_group_stop(void)
{
int stop_count;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- current->state = TASK_INTERRUPTIBLE;
- timeout = schedule_timeout(timeout);
+ timeout = schedule_timeout_interruptible(timeout);
- if (current->flags & PF_FREEZE)
- refrigerator(PF_FREEZE);
+ try_to_freeze();
spin_lock_irq(¤t->sighand->siglock);
sig = dequeue_signal(current, &these, &info);
current->blocked = current->real_blocked;
return kill_something_info(sig, &info, pid);
}
-/**
- * sys_tgkill - send signal to one specific thread
- * @tgid: the thread group ID of the thread
- * @pid: the PID of the thread
- * @sig: signal to be sent
- *
- * This syscall also checks the tgid and returns -ESRCH even if the PID
- * exists but it's not belonging to the target process anymore. This
- * method solves the problem of threads exiting and PIDs getting reused.
- */
-asmlinkage long sys_tgkill(int tgid, int pid, int sig)
+static int do_tkill(int tgid, int pid, int sig)
{
- struct siginfo info;
int error;
+ struct siginfo info;
struct task_struct *p;
- /* This is only valid for single tasks */
- if (pid <= 0 || tgid <= 0)
- return -EINVAL;
-
+ error = -ESRCH;
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_TKILL;
read_lock(&tasklist_lock);
p = find_task_by_pid(pid);
- error = -ESRCH;
- if (p && (p->tgid == tgid)) {
+ if (p && (tgid <= 0 || p->tgid == tgid)) {
error = check_kill_permission(sig, &info, p);
/*
* The null signal is a permissions and process existence
}
}
read_unlock(&tasklist_lock);
+
return error;
}
+/**
+ * sys_tgkill - send signal to one specific thread
+ * @tgid: the thread group ID of the thread
+ * @pid: the PID of the thread
+ * @sig: signal to be sent
+ *
+ * This syscall also checks the tgid and returns -ESRCH even if the PID
+ * exists but it's not belonging to the target process anymore. This
+ * method solves the problem of threads exiting and PIDs getting reused.
+ */
+asmlinkage long sys_tgkill(int tgid, int pid, int sig)
+{
+ /* This is only valid for single tasks */
+ if (pid <= 0 || tgid <= 0)
+ return -EINVAL;
+
+ return do_tkill(tgid, pid, sig);
+}
+
/*
* Send a signal to only one task, even if it's a CLONE_THREAD task.
*/
asmlinkage long
sys_tkill(int pid, int sig)
{
- struct siginfo info;
- int error;
- struct task_struct *p;
-
/* This is only valid for single tasks */
if (pid <= 0)
return -EINVAL;
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = SI_TKILL;
- info.si_pid = current->tgid;
- info.si_uid = current->uid;
-
- read_lock(&tasklist_lock);
- p = find_task_by_pid(pid);
- error = -ESRCH;
- if (p) {
- error = check_kill_permission(sig, &info, p);
- /*
- * The null signal is a permissions and process existence
- * probe. No signal is actually delivered.
- */
- if (!error && sig && p->sighand) {
- spin_lock_irq(&p->sighand->siglock);
- handle_stop_signal(sig, p);
- error = specific_send_sig_info(sig, &info, p);
- spin_unlock_irq(&p->sighand->siglock);
- }
- }
- read_unlock(&tasklist_lock);
- return error;
+ return do_tkill(0, pid, sig);
}
asmlinkage long
}
int
-do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
+do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
struct k_sigaction *k;
+ sigset_t mask;
if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
return -EINVAL;
*oact = *k;
if (act) {
+ sigdelsetmask(&act->sa.sa_mask,
+ sigmask(SIGKILL) | sigmask(SIGSTOP));
/*
* POSIX 3.3.1.3:
* "Setting a signal action to SIG_IGN for a signal that is
read_lock(&tasklist_lock);
spin_lock_irq(&t->sighand->siglock);
*k = *act;
- sigdelsetmask(&k->sa.sa_mask,
- sigmask(SIGKILL) | sigmask(SIGSTOP));
- rm_from_queue(sigmask(sig), &t->signal->shared_pending);
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ rm_from_queue_full(&mask, &t->signal->shared_pending);
do {
- rm_from_queue(sigmask(sig), &t->pending);
+ rm_from_queue_full(&mask, &t->pending);
recalc_sigpending_tsk(t);
t = next_thread(t);
} while (t != current);
}
*k = *act;
- sigdelsetmask(&k->sa.sa_mask,
- sigmask(SIGKILL) | sigmask(SIGSTOP));
}
spin_unlock_irq(¤t->sighand->siglock);
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
+ sigemptyset(&new_sa.sa.sa_mask);
ret = do_sigaction(sig, &new_sa, &old_sa);
#endif
+#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND
+asmlinkage long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+{
+ sigset_t newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP));
+
+ spin_lock_irq(¤t->sighand->siglock);
+ current->saved_sigmask = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_thread_flag(TIF_RESTORE_SIGMASK);
+ return -ERESTARTNOHAND;
+}
+#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */
+
void __init signals_init(void)
{
sigqueue_cachep =