diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 18:50:11 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 18:50:11 -0800 | 
| commit | 9e2d59ad580d590134285f361a0e80f0e98c0207 (patch) | |
| tree | f3232be75781484193413f32ec82c21f6d8eb76e /kernel/signal.c | |
| parent | 5ce1a70e2f00f0bce0cab57f798ca354b9496169 (diff) | |
| parent | 235b80226b986dabcbba844968f7807866bd0bfe (diff) | |
| download | olio-linux-3.10-9e2d59ad580d590134285f361a0e80f0e98c0207.tar.xz olio-linux-3.10-9e2d59ad580d590134285f361a0e80f0e98c0207.zip  | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
Pull signal handling cleanups from Al Viro:
 "This is the first pile; another one will come a bit later and will
  contain SYSCALL_DEFINE-related patches.
   - a bunch of signal-related syscalls (both native and compat)
     unified.
   - a bunch of compat syscalls switched to COMPAT_SYSCALL_DEFINE
     (fixing several potential problems with missing argument
     validation, while we are at it)
   - a lot of now-pointless wrappers killed
   - a couple of architectures (cris and hexagon) forgot to save
     altstack settings into sigframe, even though they used the
     (uninitialized) values in sigreturn; fixed.
   - microblaze fixes for delivery of multiple signals arriving at once
   - saner set of helpers for signal delivery introduced, several
     architectures switched to using those."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal: (143 commits)
  x86: convert to ksignal
  sparc: convert to ksignal
  arm: switch to struct ksignal * passing
  alpha: pass k_sigaction and siginfo_t using ksignal pointer
  burying unused conditionals
  make do_sigaltstack() static
  arm64: switch to generic old sigaction() (compat-only)
  arm64: switch to generic compat rt_sigaction()
  arm64: switch compat to generic old sigsuspend
  arm64: switch to generic compat rt_sigqueueinfo()
  arm64: switch to generic compat rt_sigpending()
  arm64: switch to generic compat rt_sigprocmask()
  arm64: switch to generic sigaltstack
  sparc: switch to generic old sigsuspend
  sparc: COMPAT_SYSCALL_DEFINE does all sign-extension as well as SYSCALL_DEFINE
  sparc: kill sign-extending wrappers for native syscalls
  kill sparc32_open()
  sparc: switch to use of generic old sigaction
  sparc: switch sys_compat_rt_sigaction() to COMPAT_SYSCALL_DEFINE
  mips: switch to generic sys_fork() and sys_clone()
  ...
Diffstat (limited to 'kernel/signal.c')
| -rw-r--r-- | kernel/signal.c | 332 | 
1 files changed, 293 insertions, 39 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 7f82adbad48..2a7ae296318 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2399,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 @@ -2616,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;  }  /** @@ -2646,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) @@ -2927,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 @@ -2937,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) @@ -2985,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; @@ -3030,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; @@ -3095,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)  { @@ -3118,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) @@ -3168,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 @@ -3178,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 @@ -3234,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 @@ -3268,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 @@ -3336,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 @@ -3355,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)  {  |