diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 82 | 
1 files changed, 61 insertions, 21 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index d8034737db4c..d81f4952eebb 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -27,7 +27,7 @@  #include <linux/freezer.h>  #include <linux/pid_namespace.h>  #include <linux/nsproxy.h> -#include <trace/sched.h> +#include <trace/events/sched.h>  #include <asm/param.h>  #include <asm/uaccess.h> @@ -41,8 +41,6 @@  static struct kmem_cache *sigqueue_cachep; -DEFINE_TRACE(sched_signal_send); -  static void __user *sig_handler(struct task_struct *t, int sig)  {  	return t->sighand->action[sig - 1].sa.sa_handler; @@ -249,14 +247,19 @@ void flush_sigqueue(struct sigpending *queue)  /*   * Flush all pending signals for a task.   */ +void __flush_signals(struct task_struct *t) +{ +	clear_tsk_thread_flag(t, TIF_SIGPENDING); +	flush_sigqueue(&t->pending); +	flush_sigqueue(&t->signal->shared_pending); +} +  void flush_signals(struct task_struct *t)  {  	unsigned long flags;  	spin_lock_irqsave(&t->sighand->siglock, flags); -	clear_tsk_thread_flag(t, TIF_SIGPENDING); -	flush_sigqueue(&t->pending); -	flush_sigqueue(&t->signal->shared_pending); +	__flush_signals(t);  	spin_unlock_irqrestore(&t->sighand->siglock, flags);  } @@ -829,6 +832,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  {  	struct sigpending *pending;  	struct sigqueue *q; +	int override_rlimit;  	trace_sched_signal_send(sig, t); @@ -860,9 +864,13 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  	   make sure at least one signal gets delivered and don't  	   pass on the info struct.  */ -	q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN && -					     (is_si_special(info) || -					      info->si_code >= 0))); +	if (sig < SIGRTMIN) +		override_rlimit = (is_si_special(info) || info->si_code >= 0); +	else +		override_rlimit = 0; + +	q = __sigqueue_alloc(t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE, +		override_rlimit);  	if (q) {  		list_add_tail(&q->list, &pending->list);  		switch ((unsigned long) info) { @@ -2278,24 +2286,17 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)  	return kill_something_info(sig, &info, pid);  } -static int do_tkill(pid_t tgid, pid_t pid, int sig) +static int +do_send_specific(pid_t tgid, pid_t pid, int sig, struct siginfo *info)  { -	int error; -	struct siginfo info;  	struct task_struct *p;  	unsigned long flags; - -	error = -ESRCH; -	info.si_signo = sig; -	info.si_errno = 0; -	info.si_code = SI_TKILL; -	info.si_pid = task_tgid_vnr(current); -	info.si_uid = current_uid(); +	int error = -ESRCH;  	rcu_read_lock();  	p = find_task_by_vpid(pid);  	if (p && (tgid <= 0 || task_tgid_vnr(p) == tgid)) { -		error = check_kill_permission(sig, &info, p); +		error = check_kill_permission(sig, info, p);  		/*  		 * The null signal is a permissions and process existence  		 * probe.  No signal is actually delivered. @@ -2305,7 +2306,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)  		 * signal is private anyway.  		 */  		if (!error && sig && lock_task_sighand(p, &flags)) { -			error = specific_send_sig_info(sig, &info, p); +			error = specific_send_sig_info(sig, info, p);  			unlock_task_sighand(p, &flags);  		}  	} @@ -2314,6 +2315,19 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)  	return error;  } +static int do_tkill(pid_t tgid, pid_t pid, int sig) +{ +	struct siginfo info; + +	info.si_signo = sig; +	info.si_errno = 0; +	info.si_code = SI_TKILL; +	info.si_pid = task_tgid_vnr(current); +	info.si_uid = current_uid(); + +	return do_send_specific(tgid, pid, sig, &info); +} +  /**   *  sys_tgkill - send signal to one specific thread   *  @tgid: the thread group ID of the thread @@ -2363,6 +2377,32 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig,  	return kill_proc_info(sig, &info, pid);  } +long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) +{ +	/* This is only valid for single tasks */ +	if (pid <= 0 || tgid <= 0) +		return -EINVAL; + +	/* Not even root can pretend to send signals from the kernel. +	   Nor can they impersonate a kill(), which adds source info.  */ +	if (info->si_code >= 0) +		return -EPERM; +	info->si_signo = sig; + +	return do_send_specific(tgid, pid, sig, info); +} + +SYSCALL_DEFINE4(rt_tgsigqueueinfo, pid_t, tgid, pid_t, pid, int, sig, +		siginfo_t __user *, uinfo) +{ +	siginfo_t info; + +	if (copy_from_user(&info, uinfo, sizeof(siginfo_t))) +		return -EFAULT; + +	return do_rt_tgsigqueueinfo(tgid, pid, sig, &info); +} +  int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)  {  	struct task_struct *t = current;  |