diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 55 | 
1 files changed, 37 insertions, 18 deletions
| diff --git a/kernel/signal.c b/kernel/signal.c index c73c4284160e..d523da02dd14 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -58,21 +58,20 @@ static int sig_handler_ignored(void __user *handler, int sig)  		(handler == SIG_DFL && sig_kernel_ignore(sig));  } -static int sig_task_ignored(struct task_struct *t, int sig, -		int from_ancestor_ns) +static int sig_task_ignored(struct task_struct *t, int sig, bool force)  {  	void __user *handler;  	handler = sig_handler(t, sig);  	if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) && -			handler == SIG_DFL && !from_ancestor_ns) +			handler == SIG_DFL && !force)  		return 1;  	return sig_handler_ignored(handler, sig);  } -static int sig_ignored(struct task_struct *t, int sig, int from_ancestor_ns) +static int sig_ignored(struct task_struct *t, int sig, bool force)  {  	/*  	 * Blocked signals are never ignored, since the @@ -82,7 +81,7 @@ static int sig_ignored(struct task_struct *t, int sig, int from_ancestor_ns)  	if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))  		return 0; -	if (!sig_task_ignored(t, sig, from_ancestor_ns)) +	if (!sig_task_ignored(t, sig, force))  		return 0;  	/* @@ -855,7 +854,7 @@ static void ptrace_trap_notify(struct task_struct *t)   * Returns true if the signal should be actually delivered, otherwise   * it should be dropped.   */ -static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns) +static int prepare_signal(int sig, struct task_struct *p, bool force)  {  	struct signal_struct *signal = p->signal;  	struct task_struct *t; @@ -915,7 +914,7 @@ static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns)  		}  	} -	return !sig_ignored(p, sig, from_ancestor_ns); +	return !sig_ignored(p, sig, force);  }  /* @@ -1054,13 +1053,14 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  	struct sigpending *pending;  	struct sigqueue *q;  	int override_rlimit; - -	trace_signal_generate(sig, info, t); +	int ret = 0, result;  	assert_spin_locked(&t->sighand->siglock); -	if (!prepare_signal(sig, t, from_ancestor_ns)) -		return 0; +	result = TRACE_SIGNAL_IGNORED; +	if (!prepare_signal(sig, t, +			from_ancestor_ns || (info == SEND_SIG_FORCED))) +		goto ret;  	pending = group ? &t->signal->shared_pending : &t->pending;  	/* @@ -1068,8 +1068,11 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  	 * exactly one non-rt signal, so that we can get more  	 * detailed information about the cause of the signal.  	 */ +	result = TRACE_SIGNAL_ALREADY_PENDING;  	if (legacy_queue(pending, sig)) -		return 0; +		goto ret; + +	result = TRACE_SIGNAL_DELIVERED;  	/*  	 * fast-pathed signals for kernel-internal things like SIGSTOP  	 * or SIGKILL. @@ -1127,14 +1130,15 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  			 * signal was rt and sent by user using something  			 * other than kill().  			 */ -			trace_signal_overflow_fail(sig, group, info); -			return -EAGAIN; +			result = TRACE_SIGNAL_OVERFLOW_FAIL; +			ret = -EAGAIN; +			goto ret;  		} else {  			/*  			 * This is a silent loss of information.  We still  			 * send the signal, but the *info bits are lost.  			 */ -			trace_signal_lose_info(sig, group, info); +			result = TRACE_SIGNAL_LOSE_INFO;  		}  	} @@ -1142,7 +1146,9 @@ out_set:  	signalfd_notify(t, sig);  	sigaddset(&pending->signal, sig);  	complete_signal(sig, t, group); -	return 0; +ret: +	trace_signal_generate(sig, info, t, group, result); +	return ret;  }  static int send_signal(int sig, struct siginfo *info, struct task_struct *t, @@ -1585,7 +1591,7 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)  	int sig = q->info.si_signo;  	struct sigpending *pending;  	unsigned long flags; -	int ret; +	int ret, result;  	BUG_ON(!(q->flags & SIGQUEUE_PREALLOC)); @@ -1594,7 +1600,8 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)  		goto ret;  	ret = 1; /* the signal is ignored */ -	if (!prepare_signal(sig, t, 0)) +	result = TRACE_SIGNAL_IGNORED; +	if (!prepare_signal(sig, t, false))  		goto out;  	ret = 0; @@ -1605,6 +1612,7 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)  		 */  		BUG_ON(q->info.si_code != SI_TIMER);  		q->info.si_overrun++; +		result = TRACE_SIGNAL_ALREADY_PENDING;  		goto out;  	}  	q->info.si_overrun = 0; @@ -1614,7 +1622,9 @@ int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group)  	list_add_tail(&q->list, &pending->list);  	sigaddset(&pending->signal, sig);  	complete_signal(sig, t, group); +	result = TRACE_SIGNAL_DELIVERED;  out: +	trace_signal_generate(sig, &q->info, t, group, result);  	unlock_task_sighand(t, &flags);  ret:  	return ret; @@ -1642,6 +1652,15 @@ bool do_notify_parent(struct task_struct *tsk, int sig)  	BUG_ON(!tsk->ptrace &&  	       (tsk->group_leader != tsk || !thread_group_empty(tsk))); +	if (sig != SIGCHLD) { +		/* +		 * This is only possible if parent == real_parent. +		 * Check if it has changed security domain. +		 */ +		if (tsk->parent_exec_id != tsk->parent->self_exec_id) +			sig = SIGCHLD; +	} +  	info.si_signo = sig;  	info.si_errno = 0;  	/* | 
