diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-12-25 18:38:15 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-02-03 15:09:23 -0500 |
commit | 08d32fe504a7670cab3190c624448695adcf70e4 (patch) | |
tree | a6fcff4e53049064fe4d9e060e2bb41ae842fb42 | |
parent | 574c4866e33d648520a8bd5bf6f573ea6e554e88 (diff) |
generic sys_compat_rt_sigaction()
Again, protected by a temporary config symbol (GENERIC_COMPAT_RT_SIGACTION);
will be gone by the end of series.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | arch/Kconfig | 3 | ||||
-rw-r--r-- | include/linux/compat.h | 24 | ||||
-rw-r--r-- | kernel/signal.c | 49 |
3 files changed, 76 insertions, 0 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 6b1df95ffefc..3b4a416fc448 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -368,6 +368,9 @@ config GENERIC_COMPAT_RT_SIGPENDING config GENERIC_COMPAT_RT_SIGQUEUEINFO bool +config GENERIC_COMPAT_RT_SIGACTION + bool + # # ABI hall of shame # diff --git a/include/linux/compat.h b/include/linux/compat.h index bbee15ef3ae9..0d53ab4f79c5 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -142,6 +142,22 @@ typedef struct { compat_sigset_word sig[_COMPAT_NSIG_WORDS]; } compat_sigset_t; +#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION +struct compat_sigaction { +#ifndef __ARCH_HAS_ODD_SIGACTION + compat_uptr_t sa_handler; + compat_ulong_t sa_flags; +#else + compat_ulong_t sa_flags; + compat_uptr_t sa_handler; +#endif +#ifdef __ARCH_HAS_SA_RESTORER + compat_uptr_t sa_restorer; +#endif + compat_sigset_t sa_mask __packed; +}; +#endif + /* * These functions operate strictly on struct compat_time* */ @@ -602,6 +618,14 @@ asmlinkage long compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, asmlinkage long compat_sys_rt_sigpending(compat_sigset_t __user *uset, compat_size_t sigsetsize); #endif +#ifndef CONFIG_ODD_RT_SIGACTION +#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION +asmlinkage long compat_sys_rt_sigaction(int, + const struct compat_sigaction __user *, + struct compat_sigaction __user *, + compat_size_t); +#endif +#endif #ifdef CONFIG_GENERIC_COMPAT_RT_SIGQUEUEINFO asmlinkage long compat_sys_rt_sigqueueinfo(compat_pid_t pid, int sig, struct compat_siginfo __user *uinfo); diff --git a/kernel/signal.c b/kernel/signal.c index 5a4aed1244fb..f3665f3de660 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3358,6 +3358,55 @@ SYSCALL_DEFINE4(rt_sigaction, int, sig, out: return ret; } +#ifdef CONFIG_COMPAT +#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION +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 #endif /* !CONFIG_ODD_RT_SIGACTION */ #ifdef __ARCH_WANT_SYS_SGETMASK |