diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 49 | 
1 files changed, 31 insertions, 18 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 80e8a6489f9..84989124baf 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -177,6 +177,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask)  	return sig;  } +/* + * allocate a new signal queue record + * - this may be called without locks if and only if t == current, otherwise an + *   appopriate lock must be held to protect t's user_struct + */  static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,  					 int override_rlimit)  { @@ -184,11 +189,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,  	struct user_struct *user;  	/* -	 * In order to avoid problems with "switch_user()", we want to make -	 * sure that the compiler doesn't re-load "t->user" +	 * We won't get problems with the target's UID changing under us +	 * because changing it requires RCU be used, and if t != current, the +	 * caller must be holding the RCU readlock (by way of a spinlock) and +	 * we use RCU protection here  	 */ -	user = t->cred->user; -	barrier(); +	user = __task_cred(t)->user;  	atomic_inc(&user->sigpending);  	if (override_rlimit ||  	    atomic_read(&user->sigpending) <= @@ -562,12 +568,13 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)  /*   * Bad permissions for sending the signal + * - the caller must hold at least the RCU read lock   */  static int check_kill_permission(int sig, struct siginfo *info,  				 struct task_struct *t)  { +	const struct cred *cred = current_cred(), *tcred;  	struct pid *sid; -	uid_t uid, euid;  	int error;  	if (!valid_signal(sig)) @@ -580,10 +587,11 @@ static int check_kill_permission(int sig, struct siginfo *info,  	if (error)  		return error; -	uid = current_uid(); -	euid = current_euid(); -	if ((euid ^ t->cred->suid) && (euid ^ t->cred->uid) && -	    (uid  ^ t->cred->suid) && (uid  ^ t->cred->uid) && +	tcred = __task_cred(t); +	if ((cred->euid ^ tcred->suid) && +	    (cred->euid ^ tcred->uid) && +	    (cred->uid  ^ tcred->suid) && +	    (cred->uid  ^ tcred->uid) &&  	    !capable(CAP_KILL)) {  		switch (sig) {  		case SIGCONT: @@ -1011,6 +1019,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long  	return sighand;  } +/* + * send signal info to all the members of a group + * - the caller must hold the RCU read lock at least + */  int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)  {  	unsigned long flags; @@ -1032,8 +1044,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)  /*   * __kill_pgrp_info() sends a signal to a process group: this is what the tty   * control characters do (^C, ^Z etc) + * - the caller must hold at least a readlock on tasklist_lock   */ -  int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)  {  	struct task_struct *p = NULL; @@ -1089,6 +1101,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,  {  	int ret = -EINVAL;  	struct task_struct *p; +	const struct cred *pcred;  	if (!valid_signal(sig))  		return ret; @@ -1099,9 +1112,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,  		ret = -ESRCH;  		goto out_unlock;  	} -	if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info))) -	    && (euid != p->cred->suid) && (euid != p->cred->uid) -	    && (uid != p->cred->suid) && (uid != p->cred->uid)) { +	pcred = __task_cred(p); +	if ((info == SEND_SIG_NOINFO || +	     (!is_si_special(info) && SI_FROMUSER(info))) && +	    euid != pcred->suid && euid != pcred->uid && +	    uid  != pcred->suid && uid  != pcred->uid) {  		ret = -EPERM;  		goto out_unlock;  	} @@ -1372,10 +1387,9 @@ int do_notify_parent(struct task_struct *tsk, int sig)  	 */  	rcu_read_lock();  	info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); +	info.si_uid = __task_cred(tsk)->uid;  	rcu_read_unlock(); -	info.si_uid = tsk->cred->uid; -  	thread_group_cputime(tsk, &cputime);  	info.si_utime = cputime_to_jiffies(cputime.utime);  	info.si_stime = cputime_to_jiffies(cputime.stime); @@ -1443,10 +1457,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why)  	 */  	rcu_read_lock();  	info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); +	info.si_uid = __task_cred(tsk)->uid;  	rcu_read_unlock(); -	info.si_uid = tsk->cred->uid; -  	info.si_utime = cputime_to_clock_t(tsk->utime);  	info.si_stime = cputime_to_clock_t(tsk->stime); @@ -1713,7 +1726,7 @@ static int ptrace_signal(int signr, siginfo_t *info,  		info->si_errno = 0;  		info->si_code = SI_USER;  		info->si_pid = task_pid_vnr(current->parent); -		info->si_uid = current->parent->cred->uid; +		info->si_uid = task_uid(current->parent);  	}  	/* If the (new) signal is now blocked, requeue it.  */  |