#include <linux/binfmts.h>
#include <linux/security.h>
#include <linux/syscalls.h>
-#include <linux/ptrace.h>
+#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>
* 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)
{
void __user * handler;
- /*
- * Tracers always want to know about signals..
- */
- if (t->ptrace & PT_PTRACED)
- return 0;
-
/*
* Blocked signals are never ignored, since the
* signal handler may change by the time it is
/* Is it explicitly or implicitly ignored? */
handler = t->sighand->action[sig-1].sa.sa_handler;
- return handler == SIG_IGN ||
- (handler == SIG_DFL && sig_kernel_ignore(sig));
+ if (handler != SIG_IGN &&
+ (handler != SIG_DFL || !sig_kernel_ignore(sig)))
+ return 0;
+
+ /* It's ignored, we can short-circuit unless a debugger wants it. */
+ return !tracehook_consider_ignored_signal(t, sig, handler);
}
/*
if (t->signal->group_stop_count > 0 ||
(freezing(t)) ||
PENDING(&t->pending, &t->blocked) ||
- PENDING(&t->signal->shared_pending, &t->blocked))
+ PENDING(&t->signal->shared_pending, &t->blocked) ||
+ tracehook_induce_sigpending(t))
set_tsk_thread_flag(t, TIF_SIGPENDING);
else
clear_tsk_thread_flag(t, TIF_SIGPENDING);
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);
}
}
}
-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
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)) {
if (!collect_signal(sig, pending, info))
sig = 0;
-
}
- recalc_sigpending();
return sig;
}
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
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 */
return error;
}
-/* forward decl */
-static void do_notify_parent_cldstop(struct task_struct *tsk, int why);
/*
* Handle magic process-wide effects of stop/continue signals.
*/
if (sig_fatal(p, sig) && !(p->signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
- (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) {
+ (sig == SIGKILL || !tracehook_consider_fatal_signal(t, sig))) {
/*
* This signal will be fatal to the whole group.
*/
}
/*
- * 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)
{
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;
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);
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;
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;
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).
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;
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)
{
/* do_notify_parent_cldstop should have been called instead. */
BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED));
- BUG_ON(!tsk->ptrace &&
- (tsk->group_leader != tsk || !thread_group_empty(tsk)));
+ BUG_ON(tsk->group_leader != tsk || !thread_group_empty(tsk));
info.si_signo = sig;
info.si_errno = 0;
psig = tsk->parent->sighand;
spin_lock_irqsave(&psig->siglock, flags);
- if (!tsk->ptrace && sig == SIGCHLD &&
+ if (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, int why)
+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 (tsk->ptrace & PT_PTRACED)
- 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;
BUG();
}
+ /*
+ * Tracing can decide that we should not do the normal notification.
+ */
+ if (tracehook_notify_cldstop(tsk, &info))
+ return;
+
+ tsk = tsk->group_leader;
+ parent = tsk->parent;
+
sighand = parent->sighand;
spin_lock_irqsave(&sighand->siglock, flags);
if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
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.
- *
- * This should be the path for all ptrace stops.
- * We always set current->last_siginfo while stopped here.
- * That makes it a way to test a stopped process for
- * being ptrace-stopped vs being job-control-stopped.
- *
- * If we actually decide not to stop at all because the tracer is gone,
- * we leave nostop_code in current->exit_code.
- */
-static void ptrace_stop(int exit_code, int nostop_code, siginfo_t *info)
-{
- /*
- * If there is a group stop in progress,
- * we must participate in the bookkeeping.
- */
- if (current->signal->group_stop_count > 0)
- --current->signal->group_stop_count;
-
- current->last_siginfo = info;
- current->exit_code = exit_code;
-
- /* Let the debugger run. */
- set_current_state(TASK_TRACED);
- spin_unlock_irq(¤t->sighand->siglock);
- try_to_freeze();
- read_lock(&tasklist_lock);
- if (may_ptrace_stop()) {
- do_notify_parent_cldstop(current, CLD_TRAPPED);
- read_unlock(&tasklist_lock);
- schedule();
- } else {
- /*
- * By the time we got the lock, our tracer went away.
- * Don't stop here.
- */
- read_unlock(&tasklist_lock);
- set_current_state(TASK_RUNNING);
- current->exit_code = nostop_code;
- }
-
- /*
- * We are back. Now reacquire the siglock before touching
- * last_siginfo, so that we are sure to have synchronized with
- * any signal-sending on another CPU that wants to examine it.
- */
- spin_lock_irq(¤t->sighand->siglock);
- current->last_siginfo = NULL;
-
- /*
- * Queued signals ignored us while we were stopped for tracing.
- * So check for any that we should take before resuming user mode.
- */
- recalc_sigpending();
-}
-
-void ptrace_notify(int exit_code)
-{
- siginfo_t info;
-
- BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
-
- memset(&info, 0, sizeof info);
- info.si_signo = SIGTRAP;
- info.si_code = exit_code;
- info.si_pid = current->pid;
- info.si_uid = current->uid;
-
- /* Let the debugger run. */
- spin_lock_irq(¤t->sighand->siglock);
- ptrace_stop(exit_code, 0, &info);
- spin_unlock_irq(¤t->sighand->siglock);
-}
-
static void
finish_stop(int stop_count)
{
* 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)) {
+ if (!tracehook_finish_stop(stop_count <= 0) && stop_count <= 0) {
read_lock(&tasklist_lock);
do_notify_parent_cldstop(current, CLD_STOPPED);
read_unlock(&tasklist_lock);
}
- schedule();
+ do {
+ schedule();
+ } while (try_to_freeze());
/*
* Now we don't run again until continued.
*/
handle_group_stop())
goto relock;
- signr = dequeue_signal(current, mask, info);
-
- if (!signr)
- break; /* will return 0 */
-
- if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
- ptrace_signal_deliver(regs, cookie);
-
- /* Let the debugger run. */
- ptrace_stop(signr, signr, info);
-
- /* We're back. Did the debugger cancel the sig? */
- signr = current->exit_code;
- if (signr == 0)
- continue;
-
- current->exit_code = 0;
-
- /* Update the siginfo structure if the signal has
- changed. If the debugger wanted something
- specific in the siginfo structure then it should
- have updated *info via PTRACE_SETSIGINFO. */
- if (signr != info->si_signo) {
- info->si_signo = signr;
- info->si_errno = 0;
- info->si_code = SI_USER;
- info->si_pid = current->parent->pid;
- info->si_uid = current->parent->uid;
- }
-
- /* If the (new) signal is now blocked, requeue it. */
- if (sigismember(¤t->blocked, signr)) {
- specific_send_sig_info(signr, info, current);
- continue;
- }
+ /*
+ * Tracing can induce an artifical signal and choose sigaction.
+ * The return value in signr determines the default action,
+ * but info->si_signo is the signal number we will report.
+ */
+ signr = tracehook_get_signal(current, regs, info, return_ka);
+ if (unlikely(signr < 0))
+ goto relock;
+ if (unlikely(signr != 0))
+ ka = return_ka;
+ else {
+ signr = dequeue_signal(current, mask, info);
+
+ if (!signr)
+ break; /* will return 0 */
+ ka = ¤t->sighand->action[signr-1];
}
- ka = ¤t->sighand->action[signr-1];
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
if (ka->sa.sa_handler != SIG_DFL) {
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 */
spin_lock_irq(¤t->sighand->siglock);
}
- if (likely(do_signal_stop(signr))) {
+ if (likely(do_signal_stop(info->si_signo))) {
/* It released the siglock. */
goto relock;
}
* first and our do_group_exit call below will use
* that value and ignore the one we pass it.
*/
- do_coredump((long)signr, signr, regs);
+ do_coredump(info->si_signo, info->si_signo, regs);
}
/*
* Death signals, no core dump.
*/
- do_group_exit(signr);
+ do_group_exit(info->si_signo);
/* NOTREACHED */
}
spin_unlock_irq(¤t->sighand->siglock);
EXPORT_SYMBOL(force_sig);
EXPORT_SYMBOL(kill_pg);
EXPORT_SYMBOL(kill_proc);
-EXPORT_SYMBOL(ptrace_notify);
EXPORT_SYMBOL(send_sig);
EXPORT_SYMBOL(send_sig_info);
EXPORT_SYMBOL(sigprocmask);
}
#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 =