diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 71 | 
1 files changed, 57 insertions, 14 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 2a74fe87c0d..d8034737db4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -55,10 +55,22 @@ static int sig_handler_ignored(void __user *handler, int sig)  		(handler == SIG_DFL && sig_kernel_ignore(sig));  } -static int sig_ignored(struct task_struct *t, int sig) +static int sig_task_ignored(struct task_struct *t, int sig, +		int from_ancestor_ns)  {  	void __user *handler; +	handler = sig_handler(t, sig); + +	if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) && +			handler == SIG_DFL && !from_ancestor_ns) +		return 1; + +	return sig_handler_ignored(handler, sig); +} + +static int sig_ignored(struct task_struct *t, int sig, int from_ancestor_ns) +{  	/*  	 * Blocked signals are never ignored, since the  	 * signal handler may change by the time it is @@ -67,14 +79,13 @@ static int sig_ignored(struct task_struct *t, int sig)  	if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))  		return 0; -	handler = sig_handler(t, sig); -	if (!sig_handler_ignored(handler, sig)) +	if (!sig_task_ignored(t, sig, from_ancestor_ns))  		return 0;  	/*  	 * Tracers may want to know about even ignored signals.  	 */ -	return !tracehook_consider_ignored_signal(t, sig, handler); +	return !tracehook_consider_ignored_signal(t, sig);  }  /* @@ -318,7 +329,7 @@ int unhandled_signal(struct task_struct *tsk, int sig)  		return 1;  	if (handler != SIG_IGN && handler != SIG_DFL)  		return 0; -	return !tracehook_consider_fatal_signal(tsk, sig, handler); +	return !tracehook_consider_fatal_signal(tsk, sig);  } @@ -624,7 +635,7 @@ static int check_kill_permission(int sig, struct siginfo *info,   * Returns true if the signal should be actually delivered, otherwise   * it should be dropped.   */ -static int prepare_signal(int sig, struct task_struct *p) +static int prepare_signal(int sig, struct task_struct *p, int from_ancestor_ns)  {  	struct signal_struct *signal = p->signal;  	struct task_struct *t; @@ -708,7 +719,7 @@ static int prepare_signal(int sig, struct task_struct *p)  		}  	} -	return !sig_ignored(p, sig); +	return !sig_ignored(p, sig, from_ancestor_ns);  }  /* @@ -777,7 +788,7 @@ static void complete_signal(int sig, struct task_struct *p, int group)  	    !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&  	    !sigismember(&t->real_blocked, sig) &&  	    (sig == SIGKILL || -	     !tracehook_consider_fatal_signal(t, sig, SIG_DFL))) { +	     !tracehook_consider_fatal_signal(t, sig))) {  		/*  		 * This signal will be fatal to the whole group.  		 */ @@ -813,8 +824,8 @@ static inline int legacy_queue(struct sigpending *signals, int sig)  	return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);  } -static int send_signal(int sig, struct siginfo *info, struct task_struct *t, -			int group) +static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, +			int group, int from_ancestor_ns)  {  	struct sigpending *pending;  	struct sigqueue *q; @@ -822,7 +833,8 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,  	trace_sched_signal_send(sig, t);  	assert_spin_locked(&t->sighand->siglock); -	if (!prepare_signal(sig, t)) + +	if (!prepare_signal(sig, t, from_ancestor_ns))  		return 0;  	pending = group ? &t->signal->shared_pending : &t->pending; @@ -871,6 +883,8 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,  			break;  		default:  			copy_siginfo(&q->info, info); +			if (from_ancestor_ns) +				q->info.si_pid = 0;  			break;  		}  	} else if (!is_si_special(info)) { @@ -889,6 +903,20 @@ out_set:  	return 0;  } +static int send_signal(int sig, struct siginfo *info, struct task_struct *t, +			int group) +{ +	int from_ancestor_ns = 0; + +#ifdef CONFIG_PID_NS +	if (!is_si_special(info) && SI_FROMUSER(info) && +			task_pid_nr_ns(current, task_active_pid_ns(t)) <= 0) +		from_ancestor_ns = 1; +#endif + +	return __send_signal(sig, info, t, group, from_ancestor_ns); +} +  int print_fatal_signals;  static void print_fatal_signal(struct pt_regs *regs, int signr) @@ -1133,7 +1161,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,  	if (sig && p->sighand) {  		unsigned long flags;  		spin_lock_irqsave(&p->sighand->siglock, flags); -		ret = __group_send_sig_info(sig, info, p); +		ret = __send_signal(sig, info, p, 1, 0);  		spin_unlock_irqrestore(&p->sighand->siglock, flags);  	}  out_unlock: @@ -1320,7 +1348,7 @@ 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)) +	if (!prepare_signal(sig, t, 0))  		goto out;  	ret = 0; @@ -1575,7 +1603,15 @@ static void ptrace_stop(int exit_code, int clear_code, siginfo_t *info)  	read_lock(&tasklist_lock);  	if (may_ptrace_stop()) {  		do_notify_parent_cldstop(current, CLD_TRAPPED); +		/* +		 * Don't want to allow preemption here, because +		 * sys_ptrace() needs this task to be inactive. +		 * +		 * XXX: implement read_unlock_no_resched(). +		 */ +		preempt_disable();  		read_unlock(&tasklist_lock); +		preempt_enable_no_resched();  		schedule();  	} else {  		/* @@ -1836,9 +1872,16 @@ relock:  		/*  		 * Global init gets no signals it doesn't want. +		 * Container-init gets no signals it doesn't want from same +		 * container. +		 * +		 * Note that if global/container-init sees a sig_kernel_only() +		 * signal here, the signal must have been generated internally +		 * or must have come from an ancestor namespace. In either +		 * case, the signal cannot be dropped.  		 */  		if (unlikely(signal->flags & SIGNAL_UNKILLABLE) && -		    !signal_group_exit(signal)) +				!sig_kernel_only(signr))  			continue;  		if (sig_kernel_stop(signr)) {  |