diff options
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 363 | 
1 files changed, 310 insertions, 53 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 53cd5c4d117..2a7ae296318 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -680,23 +680,17 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)   * No need to set need_resched since signal event passing   * goes through ->blocked   */ -void signal_wake_up(struct task_struct *t, int resume) +void signal_wake_up_state(struct task_struct *t, unsigned int state)  { -	unsigned int mask; -  	set_tsk_thread_flag(t, TIF_SIGPENDING); -  	/* -	 * For SIGKILL, we want to wake it up in the stopped/traced/killable +	 * TASK_WAKEKILL also means wake it up in the stopped/traced/killable  	 * case. We don't check t->state here because there is a race with it  	 * executing another processor and just now entering stopped state.  	 * By using wake_up_state, we ensure the process will wake up and  	 * handle its death signal.  	 */ -	mask = TASK_INTERRUPTIBLE; -	if (resume) -		mask |= TASK_WAKEKILL; -	if (!wake_up_state(t, mask)) +	if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))  		kick_process(t);  } @@ -844,7 +838,7 @@ static void ptrace_trap_notify(struct task_struct *t)  	assert_spin_locked(&t->sighand->siglock);  	task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY); -	signal_wake_up(t, t->jobctl & JOBCTL_LISTENING); +	ptrace_signal_wake_up(t, t->jobctl & JOBCTL_LISTENING);  }  /* @@ -1638,6 +1632,7 @@ bool do_notify_parent(struct task_struct *tsk, int sig)  	unsigned long flags;  	struct sighand_struct *psig;  	bool autoreap = false; +	cputime_t utime, stime;  	BUG_ON(sig == -1); @@ -1675,8 +1670,9 @@ bool do_notify_parent(struct task_struct *tsk, int sig)  				       task_uid(tsk));  	rcu_read_unlock(); -	info.si_utime = cputime_to_clock_t(tsk->utime + tsk->signal->utime); -	info.si_stime = cputime_to_clock_t(tsk->stime + tsk->signal->stime); +	task_cputime(tsk, &utime, &stime); +	info.si_utime = cputime_to_clock_t(utime + tsk->signal->utime); +	info.si_stime = cputime_to_clock_t(stime + tsk->signal->stime);  	info.si_status = tsk->exit_code & 0x7f;  	if (tsk->exit_code & 0x80) @@ -1740,6 +1736,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,  	unsigned long flags;  	struct task_struct *parent;  	struct sighand_struct *sighand; +	cputime_t utime, stime;  	if (for_ptracer) {  		parent = tsk->parent; @@ -1758,8 +1755,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,  	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); -	info.si_stime = cputime_to_clock_t(tsk->stime); +	task_cputime(tsk, &utime, &stime); +	info.si_utime = cputime_to_clock_t(utime); +	info.si_stime = cputime_to_clock_t(stime);   	info.si_code = why;   	switch (why) { @@ -1800,6 +1798,10 @@ static inline int may_ptrace_stop(void)  	 * If SIGKILL was already sent before the caller unlocked  	 * ->siglock we must see ->core_state != NULL. Otherwise it  	 * is safe to enter schedule(). +	 * +	 * This is almost outdated, a task with the pending SIGKILL can't +	 * block in TASK_TRACED. But PTRACE_EVENT_EXIT can be reported +	 * after SIGKILL was already dequeued.  	 */  	if (unlikely(current->mm->core_state) &&  	    unlikely(current->mm == current->parent->mm)) @@ -1925,6 +1927,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info)  		if (gstop_done)  			do_notify_parent_cldstop(current, false, why); +		/* tasklist protects us from ptrace_freeze_traced() */  		__set_current_state(TASK_RUNNING);  		if (clear_code)  			current->exit_code = 0; @@ -2396,6 +2399,15 @@ void signal_delivered(int sig, siginfo_t *info, struct k_sigaction *ka,  	tracehook_signal_handler(sig, info, ka, regs, stepping);  } +void signal_setup_done(int failed, struct ksignal *ksig, int stepping) +{ +	if (failed) +		force_sigsegv(ksig->sig, current); +	else +		signal_delivered(ksig->sig, &ksig->info, &ksig->ka, +			signal_pt_regs(), stepping); +} +  /*   * It could be that complete_signal() picked us to notify about the   * group-wide signal. Other threads should be notified now to take @@ -2613,28 +2625,58 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,  	return 0;  } -long do_sigpending(void __user *set, unsigned long sigsetsize) +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, +		compat_sigset_t __user *, oset, compat_size_t, sigsetsize)  { -	long error = -EINVAL; -	sigset_t pending; +#ifdef __BIG_ENDIAN +	sigset_t old_set = current->blocked; + +	/* XXX: Don't preclude handling different sized sigset_t's.  */ +	if (sigsetsize != sizeof(sigset_t)) +		return -EINVAL; + +	if (nset) { +		compat_sigset_t new32; +		sigset_t new_set; +		int error; +		if (copy_from_user(&new32, nset, sizeof(compat_sigset_t))) +			return -EFAULT; + +		sigset_from_compat(&new_set, &new32); +		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); + +		error = sigprocmask(how, &new_set, NULL); +		if (error) +			return error; +	} +	if (oset) { +		compat_sigset_t old32; +		sigset_to_compat(&old32, &old_set); +		if (copy_to_user(oset, &old_set, sizeof(sigset_t))) +			return -EFAULT; +	} +	return 0; +#else +	return sys_rt_sigprocmask(how, (sigset_t __user *)nset, +				  (sigset_t __user *)oset, sigsetsize); +#endif +} +#endif +static int do_sigpending(void *set, unsigned long sigsetsize) +{  	if (sigsetsize > sizeof(sigset_t)) -		goto out; +		return -EINVAL;  	spin_lock_irq(¤t->sighand->siglock); -	sigorsets(&pending, ¤t->pending.signal, +	sigorsets(set, ¤t->pending.signal,  		  ¤t->signal->shared_pending.signal);  	spin_unlock_irq(¤t->sighand->siglock);  	/* Outside the lock because only this thread touches it.  */ -	sigandsets(&pending, ¤t->blocked, &pending); - -	error = -EFAULT; -	if (!copy_to_user(set, &pending, sigsetsize)) -		error = 0; - -out: -	return error; +	sigandsets(set, ¤t->blocked, set); +	return 0;  }  /** @@ -2643,11 +2685,36 @@ out:   *  @set: stores pending signals   *  @sigsetsize: size of sigset_t type or larger   */ -SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, set, size_t, sigsetsize) +SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, uset, size_t, sigsetsize)  { -	return do_sigpending(set, sigsetsize); +	sigset_t set; +	int err = do_sigpending(&set, sigsetsize); +	if (!err && copy_to_user(uset, &set, sigsetsize)) +		err = -EFAULT; +	return err;  } +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset, +		compat_size_t, sigsetsize) +{ +#ifdef __BIG_ENDIAN +	sigset_t set; +	int err = do_sigpending(&set, sigsetsize); +	if (!err) { +		compat_sigset_t set32; +		sigset_to_compat(&set32, &set); +		/* we can get here only if sigsetsize <= sizeof(set) */ +		if (copy_to_user(uset, &set32, sigsetsize)) +			err = -EFAULT; +	} +	return err; +#else +	return sys_rt_sigpending((sigset_t __user *)uset, sigsetsize); +#endif +} +#endif +  #ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER  int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from) @@ -2924,6 +2991,22 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, sig)  	return do_tkill(0, pid, sig);  } +static int do_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t *info) +{ +	/* Not even root can pretend to send signals from the kernel. +	 * Nor can they impersonate a kill()/tgkill(), which adds source info. +	 */ +	if (info->si_code >= 0 || info->si_code == SI_TKILL) { +		/* We used to allow any < 0 si_code */ +		WARN_ON_ONCE(info->si_code < 0); +		return -EPERM; +	} +	info->si_signo = sig; + +	/* POSIX.1b doesn't mention process groups.  */ +	return kill_proc_info(sig, info, pid); +} +  /**   *  sys_rt_sigqueueinfo - send signal information to a signal   *  @pid: the PID of the thread @@ -2934,25 +3017,26 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, 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_sigqueueinfo(pid, sig, &info); +} -	/* Not even root can pretend to send signals from the kernel. -	 * Nor can they impersonate a kill()/tgkill(), which adds source info. -	 */ -	if (info.si_code >= 0 || info.si_code == SI_TKILL) { -		/* We used to allow any < 0 si_code */ -		WARN_ON_ONCE(info.si_code < 0); -		return -EPERM; -	} -	info.si_signo = sig; - -	/* POSIX.1b doesn't mention process groups.  */ -	return kill_proc_info(sig, &info, pid); +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE3(rt_sigqueueinfo, +			compat_pid_t, pid, +			int, sig, +			struct compat_siginfo __user *, uinfo) +{ +	siginfo_t info; +	int ret = copy_siginfo_from_user32(&info, uinfo); +	if (unlikely(ret)) +		return ret; +	return do_rt_sigqueueinfo(pid, sig, &info);  } +#endif -long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) +static int 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) @@ -2982,6 +3066,21 @@ SYSCALL_DEFINE4(rt_tgsigqueueinfo, pid_t, tgid, pid_t, pid, int, sig,  	return do_rt_tgsigqueueinfo(tgid, pid, sig, &info);  } +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(rt_tgsigqueueinfo, +			compat_pid_t, tgid, +			compat_pid_t, pid, +			int, sig, +			struct compat_siginfo __user *, uinfo) +{ +	siginfo_t info; + +	if (copy_siginfo_from_user32(&info, uinfo)) +		return -EFAULT; +	return do_rt_tgsigqueueinfo(tgid, pid, sig, &info); +} +#endif +  int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)  {  	struct task_struct *t = current; @@ -3027,7 +3126,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)  	return 0;  } -int  +static int   do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp)  {  	stack_t oss; @@ -3092,12 +3191,10 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s  out:  	return error;  } -#ifdef CONFIG_GENERIC_SIGALTSTACK  SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)  {  	return do_sigaltstack(uss, uoss, current_user_stack_pointer());  } -#endif  int restore_altstack(const stack_t __user *uss)  { @@ -3115,7 +3212,6 @@ int __save_altstack(stack_t __user *uss, unsigned long sp)  }  #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_SIGALTSTACK  COMPAT_SYSCALL_DEFINE2(sigaltstack,  			const compat_stack_t __user *, uss_ptr,  			compat_stack_t __user *, uoss_ptr) @@ -3165,7 +3261,6 @@ int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp)  		__put_user(t->sas_ss_size, &uss->ss_size);  }  #endif -#endif  #ifdef __ARCH_WANT_SYS_SIGPENDING @@ -3175,7 +3270,7 @@ int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp)   */  SYSCALL_DEFINE1(sigpending, old_sigset_t __user *, set)  { -	return do_sigpending(set, sizeof(*set)); +	return sys_rt_sigpending((sigset_t __user *)set, sizeof(old_sigset_t));   }  #endif @@ -3231,7 +3326,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,  }  #endif /* __ARCH_WANT_SYS_SIGPROCMASK */ -#ifdef __ARCH_WANT_SYS_RT_SIGACTION +#ifndef CONFIG_ODD_RT_SIGACTION  /**   *  sys_rt_sigaction - alter an action taken by a process   *  @sig: signal to be sent @@ -3265,7 +3360,132 @@ SYSCALL_DEFINE4(rt_sigaction, int, sig,  out:  	return ret;  } -#endif /* __ARCH_WANT_SYS_RT_SIGACTION */ +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, +		const struct compat_sigaction __user *, act, +		struct compat_sigaction __user *, oact, +		compat_size_t, sigsetsize) +{ +	struct k_sigaction new_ka, old_ka; +	compat_sigset_t mask; +#ifdef __ARCH_HAS_SA_RESTORER +	compat_uptr_t restorer; +#endif +	int ret; + +	/* XXX: Don't preclude handling different sized sigset_t's.  */ +	if (sigsetsize != sizeof(compat_sigset_t)) +		return -EINVAL; + +	if (act) { +		compat_uptr_t handler; +		ret = get_user(handler, &act->sa_handler); +		new_ka.sa.sa_handler = compat_ptr(handler); +#ifdef __ARCH_HAS_SA_RESTORER +		ret |= get_user(restorer, &act->sa_restorer); +		new_ka.sa.sa_restorer = compat_ptr(restorer); +#endif +		ret |= copy_from_user(&mask, &act->sa_mask, sizeof(mask)); +		ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); +		if (ret) +			return -EFAULT; +		sigset_from_compat(&new_ka.sa.sa_mask, &mask); +	} + +	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); +	if (!ret && oact) { +		sigset_to_compat(&mask, &old_ka.sa.sa_mask); +		ret = put_user(ptr_to_compat(old_ka.sa.sa_handler),  +			       &oact->sa_handler); +		ret |= copy_to_user(&oact->sa_mask, &mask, sizeof(mask)); +		ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); +#ifdef __ARCH_HAS_SA_RESTORER +		ret |= put_user(ptr_to_compat(old_ka.sa.sa_restorer), +				&oact->sa_restorer); +#endif +	} +	return ret; +} +#endif +#endif /* !CONFIG_ODD_RT_SIGACTION */ + +#ifdef CONFIG_OLD_SIGACTION +SYSCALL_DEFINE3(sigaction, int, sig, +		const struct old_sigaction __user *, act, +	        struct old_sigaction __user *, oact) +{ +	struct k_sigaction new_ka, old_ka; +	int ret; + +	if (act) { +		old_sigset_t mask; +		if (!access_ok(VERIFY_READ, act, sizeof(*act)) || +		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) || +		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || +		    __get_user(new_ka.sa.sa_flags, &act->sa_flags) || +		    __get_user(mask, &act->sa_mask)) +			return -EFAULT; +#ifdef __ARCH_HAS_KA_RESTORER +		new_ka.ka_restorer = NULL; +#endif +		siginitset(&new_ka.sa.sa_mask, mask); +	} + +	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + +	if (!ret && oact) { +		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || +		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || +		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || +		    __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || +		    __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) +			return -EFAULT; +	} + +	return ret; +} +#endif +#ifdef CONFIG_COMPAT_OLD_SIGACTION +COMPAT_SYSCALL_DEFINE3(sigaction, int, sig, +		const struct compat_old_sigaction __user *, act, +	        struct compat_old_sigaction __user *, oact) +{ +	struct k_sigaction new_ka, old_ka; +	int ret; +	compat_old_sigset_t mask; +	compat_uptr_t handler, restorer; + +	if (act) { +		if (!access_ok(VERIFY_READ, act, sizeof(*act)) || +		    __get_user(handler, &act->sa_handler) || +		    __get_user(restorer, &act->sa_restorer) || +		    __get_user(new_ka.sa.sa_flags, &act->sa_flags) || +		    __get_user(mask, &act->sa_mask)) +			return -EFAULT; + +#ifdef __ARCH_HAS_KA_RESTORER +		new_ka.ka_restorer = NULL; +#endif +		new_ka.sa.sa_handler = compat_ptr(handler); +		new_ka.sa.sa_restorer = compat_ptr(restorer); +		siginitset(&new_ka.sa.sa_mask, mask); +	} + +	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + +	if (!ret && oact) { +		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || +		    __put_user(ptr_to_compat(old_ka.sa.sa_handler), +			       &oact->sa_handler) || +		    __put_user(ptr_to_compat(old_ka.sa.sa_restorer), +			       &oact->sa_restorer) || +		    __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || +		    __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) +			return -EFAULT; +	} +	return ret; +} +#endif  #ifdef __ARCH_WANT_SYS_SGETMASK @@ -3333,7 +3553,6 @@ int sigsuspend(sigset_t *set)  	return -ERESTARTNOHAND;  } -#ifdef __ARCH_WANT_SYS_RT_SIGSUSPEND  /**   *  sys_rt_sigsuspend - replace the signal mask for a value with the   *	@unewset value until a signal is received @@ -3352,7 +3571,45 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)  		return -EFAULT;  	return sigsuspend(&newset);  } -#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */ +  +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE2(rt_sigsuspend, compat_sigset_t __user *, unewset, compat_size_t, sigsetsize) +{ +#ifdef __BIG_ENDIAN +	sigset_t newset; +	compat_sigset_t newset32; + +	/* XXX: Don't preclude handling different sized sigset_t's.  */ +	if (sigsetsize != sizeof(sigset_t)) +		return -EINVAL; + +	if (copy_from_user(&newset32, unewset, sizeof(compat_sigset_t))) +		return -EFAULT; +	sigset_from_compat(&newset, &newset32); +	return sigsuspend(&newset); +#else +	/* on little-endian bitmaps don't care about granularity */ +	return sys_rt_sigsuspend((sigset_t __user *)unewset, sigsetsize); +#endif +} +#endif + +#ifdef CONFIG_OLD_SIGSUSPEND +SYSCALL_DEFINE1(sigsuspend, old_sigset_t, mask) +{ +	sigset_t blocked; +	siginitset(&blocked, mask); +	return sigsuspend(&blocked); +} +#endif +#ifdef CONFIG_OLD_SIGSUSPEND3 +SYSCALL_DEFINE3(sigsuspend, int, unused1, int, unused2, old_sigset_t, mask) +{ +	sigset_t blocked; +	siginitset(&blocked, mask); +	return sigsuspend(&blocked); +} +#endif  __attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)  {  |