diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 141 | 
1 files changed, 81 insertions, 60 deletions
| diff --git a/kernel/signal.c b/kernel/signal.c index d523da02dd14..677102789cf2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -29,6 +29,7 @@  #include <linux/pid_namespace.h>  #include <linux/nsproxy.h>  #include <linux/user_namespace.h> +#include <linux/uprobes.h>  #define CREATE_TRACE_POINTS  #include <trace/events/signal.h> @@ -36,6 +37,7 @@  #include <asm/uaccess.h>  #include <asm/unistd.h>  #include <asm/siginfo.h> +#include <asm/cacheflush.h>  #include "audit.h"	/* audit_signal_info() */  /* @@ -159,7 +161,7 @@ void recalc_sigpending(void)  #define SYNCHRONOUS_MASK \  	(sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \ -	 sigmask(SIGTRAP) | sigmask(SIGFPE)) +	 sigmask(SIGTRAP) | sigmask(SIGFPE) | sigmask(SIGSYS))  int next_signal(struct sigpending *pending, sigset_t *mask)  { @@ -766,14 +768,13 @@ static int kill_ok_by_cred(struct task_struct *t)  	const struct cred *cred = current_cred();  	const struct cred *tcred = __task_cred(t); -	if (cred->user->user_ns == tcred->user->user_ns && -	    (cred->euid == tcred->suid || -	     cred->euid == tcred->uid || -	     cred->uid  == tcred->suid || -	     cred->uid  == tcred->uid)) +	if (uid_eq(cred->euid, tcred->suid) || +	    uid_eq(cred->euid, tcred->uid)  || +	    uid_eq(cred->uid,  tcred->suid) || +	    uid_eq(cred->uid,  tcred->uid))  		return 1; -	if (ns_capable(tcred->user->user_ns, CAP_KILL)) +	if (ns_capable(tcred->user_ns, CAP_KILL))  		return 1;  	return 0; @@ -1019,15 +1020,6 @@ static inline int legacy_queue(struct sigpending *signals, int sig)  	return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);  } -/* - * map the uid in struct cred into user namespace *ns - */ -static inline uid_t map_cred_ns(const struct cred *cred, -				struct user_namespace *ns) -{ -	return user_ns_map_uid(ns, cred, cred->uid); -} -  #ifdef CONFIG_USER_NS  static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_struct *t)  { @@ -1037,8 +1029,10 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str  	if (SI_FROMKERNEL(info))  		return; -	info->si_uid = user_ns_map_uid(task_cred_xxx(t, user_ns), -					current_cred(), info->si_uid); +	rcu_read_lock(); +	info->si_uid = from_kuid_munged(task_cred_xxx(t, user_ns), +					make_kuid(current_user_ns(), info->si_uid)); +	rcu_read_unlock();  }  #else  static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_struct *t) @@ -1105,7 +1099,7 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,  			q->info.si_code = SI_USER;  			q->info.si_pid = task_tgid_nr_ns(current,  							task_active_pid_ns(t)); -			q->info.si_uid = current_uid(); +			q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());  			break;  		case (unsigned long) SEND_SIG_PRIV:  			q->info.si_signo = sig; @@ -1386,10 +1380,8 @@ static int kill_as_cred_perm(const struct cred *cred,  			     struct task_struct *target)  {  	const struct cred *pcred = __task_cred(target); -	if (cred->user_ns != pcred->user_ns) -		return 0; -	if (cred->euid != pcred->suid && cred->euid != pcred->uid && -	    cred->uid  != pcred->suid && cred->uid  != pcred->uid) +	if (!uid_eq(cred->euid, pcred->suid) && !uid_eq(cred->euid, pcred->uid) && +	    !uid_eq(cred->uid,  pcred->suid) && !uid_eq(cred->uid,  pcred->uid))  		return 0;  	return 1;  } @@ -1664,21 +1656,20 @@ bool do_notify_parent(struct task_struct *tsk, int sig)  	info.si_signo = sig;  	info.si_errno = 0;  	/* -	 * we are under tasklist_lock here so our parent is tied to -	 * us and cannot exit and release its namespace. +	 * We are under tasklist_lock here so our parent is tied to +	 * us and cannot change.  	 * -	 * the only it can is to switch its nsproxy with sys_unshare, -	 * bu uncharing pid namespaces is not allowed, so we'll always -	 * see relevant namespace +	 * task_active_pid_ns will always return the same pid namespace +	 * until a task passes through release_task.  	 *  	 * write_lock() currently calls preempt_disable() which is the  	 * same as rcu_read_lock(), but according to Oleg, this is not  	 * correct to rely on this  	 */  	rcu_read_lock(); -	info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); -	info.si_uid = map_cred_ns(__task_cred(tsk), -			task_cred_xxx(tsk->parent, user_ns)); +	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent)); +	info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns), +				       task_uid(tsk));  	rcu_read_unlock();  	info.si_utime = cputime_to_clock_t(tsk->utime + tsk->signal->utime); @@ -1761,8 +1752,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,  	 */  	rcu_read_lock();  	info.si_pid = task_pid_nr_ns(tsk, parent->nsproxy->pid_ns); -	info.si_uid = map_cred_ns(__task_cred(tsk), -			task_cred_xxx(parent, user_ns)); +	info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));  	rcu_read_unlock();  	info.si_utime = cputime_to_clock_t(tsk->utime); @@ -1972,7 +1962,7 @@ static void ptrace_do_notify(int signr, int exit_code, int why)  	info.si_signo = signr;  	info.si_code = exit_code;  	info.si_pid = task_pid_vnr(current); -	info.si_uid = current_uid(); +	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());  	/* Let the debugger run.  */  	ptrace_stop(exit_code, why, 1, &info); @@ -2180,8 +2170,8 @@ static int ptrace_signal(int signr, siginfo_t *info,  		info->si_code = SI_USER;  		rcu_read_lock();  		info->si_pid = task_pid_vnr(current->parent); -		info->si_uid = map_cred_ns(__task_cred(current->parent), -				current_user_ns()); +		info->si_uid = from_kuid_munged(current_user_ns(), +						task_uid(current->parent));  		rcu_read_unlock();  	} @@ -2201,6 +2191,9 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,  	struct signal_struct *signal = current->signal;  	int signr; +	if (unlikely(uprobe_deny_signal())) +		return 0; +  relock:  	/*  	 * We'll jump back here after any time we were stopped in TASK_STOPPED. @@ -2375,24 +2368,34 @@ relock:  }  /** - * block_sigmask - add @ka's signal mask to current->blocked - * @ka: action for @signr - * @signr: signal that has been successfully delivered + * signal_delivered -  + * @sig:		number of signal being delivered + * @info:		siginfo_t of signal being delivered + * @ka:			sigaction setting that chose the handler + * @regs:		user register state + * @stepping:		nonzero if debugger single-step or block-step in use   *   * This function should be called when a signal has succesfully been - * delivered. It adds the mask of signals for @ka to current->blocked - * so that they are blocked during the execution of the signal - * handler. In addition, @signr will be blocked unless %SA_NODEFER is - * set in @ka->sa.sa_flags. + * delivered. It updates the blocked signals accordingly (@ka->sa.sa_mask + * is always blocked, and the signal itself is blocked unless %SA_NODEFER + * is set in @ka->sa.sa_flags.  Tracing is notified.   */ -void block_sigmask(struct k_sigaction *ka, int signr) +void signal_delivered(int sig, siginfo_t *info, struct k_sigaction *ka, +			struct pt_regs *regs, int stepping)  {  	sigset_t blocked; +	/* A signal was successfully delivered, and the +	   saved sigmask was stored on the signal frame, +	   and will be restored by sigreturn.  So we can +	   simply clear the restore sigmask flag.  */ +	clear_restore_sigmask(); +  	sigorsets(&blocked, ¤t->blocked, &ka->sa.sa_mask);  	if (!(ka->sa.sa_flags & SA_NODEFER)) -		sigaddset(&blocked, signr); +		sigaddset(&blocked, sig);  	set_current_blocked(&blocked); +	tracehook_signal_handler(sig, info, ka, regs, stepping);  }  /* @@ -2525,7 +2528,16 @@ static void __set_task_blocked(struct task_struct *tsk, const sigset_t *newset)   * It is wrong to change ->blocked directly, this helper should be used   * to ensure the process can't miss a shared signal we are going to block.   */ -void set_current_blocked(const sigset_t *newset) +void set_current_blocked(sigset_t *newset) +{ +	struct task_struct *tsk = current; +	sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP)); +	spin_lock_irq(&tsk->sighand->siglock); +	__set_task_blocked(tsk, newset); +	spin_unlock_irq(&tsk->sighand->siglock); +} + +void __set_current_blocked(const sigset_t *newset)  {  	struct task_struct *tsk = current; @@ -2565,7 +2577,7 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)  		return -EINVAL;  	} -	set_current_blocked(&newset); +	__set_current_blocked(&newset);  	return 0;  } @@ -2705,6 +2717,13 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)  		err |= __put_user(from->si_uid, &to->si_uid);  		err |= __put_user(from->si_ptr, &to->si_ptr);  		break; +#ifdef __ARCH_SIGSYS +	case __SI_SYS: +		err |= __put_user(from->si_call_addr, &to->si_call_addr); +		err |= __put_user(from->si_syscall, &to->si_syscall); +		err |= __put_user(from->si_arch, &to->si_arch); +		break; +#endif  	default: /* this is just in case for now ... */  		err |= __put_user(from->si_pid, &to->si_pid);  		err |= __put_user(from->si_uid, &to->si_uid); @@ -2827,7 +2846,7 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)  	info.si_errno = 0;  	info.si_code = SI_USER;  	info.si_pid = task_tgid_vnr(current); -	info.si_uid = current_uid(); +	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());  	return kill_something_info(sig, &info, pid);  } @@ -2870,7 +2889,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)  	info.si_errno = 0;  	info.si_code = SI_TKILL;  	info.si_pid = task_tgid_vnr(current); -	info.si_uid = current_uid(); +	info.si_uid = from_kuid_munged(current_user_ns(), current_uid());  	return do_send_specific(tgid, pid, sig, &info);  } @@ -3132,7 +3151,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,  			return -EINVAL;  		} -		set_current_blocked(&new_blocked); +		__set_current_blocked(&new_blocked);  	}  	if (oset) { @@ -3196,7 +3215,6 @@ SYSCALL_DEFINE1(ssetmask, int, newmask)  	int old = current->blocked.sig[0];  	sigset_t newset; -	siginitset(&newset, newmask & ~(sigmask(SIGKILL) | sigmask(SIGSTOP)));  	set_current_blocked(&newset);  	return old; @@ -3235,6 +3253,17 @@ SYSCALL_DEFINE0(pause)  #endif +int sigsuspend(sigset_t *set) +{ +	current->saved_sigmask = current->blocked; +	set_current_blocked(set); + +	current->state = TASK_INTERRUPTIBLE; +	schedule(); +	set_restore_sigmask(); +	return -ERESTARTNOHAND; +} +  #ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND  /**   *  sys_rt_sigsuspend - replace the signal mask for a value with the @@ -3252,15 +3281,7 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)  	if (copy_from_user(&newset, unewset, sizeof(newset)))  		return -EFAULT; -	sigdelsetmask(&newset, sigmask(SIGKILL)|sigmask(SIGSTOP)); - -	current->saved_sigmask = current->blocked; -	set_current_blocked(&newset); - -	current->state = TASK_INTERRUPTIBLE; -	schedule(); -	set_restore_sigmask(); -	return -ERESTARTNOHAND; +	return sigsuspend(&newset);  }  #endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ | 
