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() ...
This commit is contained in:
334
kernel/signal.c
334
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;
|
||||
|
||||
/* 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);
|
||||
return do_rt_sigqueueinfo(pid, sig, &info);
|
||||
}
|
||||
|
||||
long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info)
|
||||
#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
|
||||
|
||||
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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user