summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/Kconfig3
-rw-r--r--include/linux/compat.h4
-rw-r--r--include/linux/signal.h1
-rw-r--r--kernel/signal.c52
4 files changed, 42 insertions, 18 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index 374a68adbf1..18c0383dcc4 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -362,6 +362,9 @@ config GENERIC_SIGALTSTACK
config GENERIC_COMPAT_RT_SIGPROCMASK
bool
+config GENERIC_COMPAT_RT_SIGPENDING
+ bool
+
#
# ABI hall of shame
#
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 9d3c2a98d53..75548a43a1c 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -598,6 +598,10 @@ asmlinkage long compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set,
compat_sigset_t __user *oset,
compat_size_t sigsetsize);
#endif
+#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPENDING
+asmlinkage long compat_sys_rt_sigpending(compat_sigset_t __user *uset,
+ compat_size_t sigsetsize);
+#endif
asmlinkage long compat_sys_sysinfo(struct compat_sysinfo __user *info);
asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg);
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 0a89ffc4846..786bd99fde6 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -243,7 +243,6 @@ extern int group_send_sig_info(int sig, struct siginfo *info, struct task_struct
extern int __group_send_sig_info(int, struct siginfo *, struct task_struct *);
extern long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig,
siginfo_t *info);
-extern long do_sigpending(void __user *, unsigned long);
extern int do_sigtimedwait(const sigset_t *, siginfo_t *,
const struct timespec *);
extern int sigprocmask(int, sigset_t *, sigset_t *);
diff --git a/kernel/signal.c b/kernel/signal.c
index bce1222b731..3040c349b0e 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2654,28 +2654,19 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset,
#endif
#endif
-long do_sigpending(void __user *set, unsigned long sigsetsize)
+static int do_sigpending(void *set, unsigned long sigsetsize)
{
- long error = -EINVAL;
- sigset_t pending;
-
if (sigsetsize > sizeof(sigset_t))
- goto out;
+ return -EINVAL;
spin_lock_irq(&current->sighand->siglock);
- sigorsets(&pending, &current->pending.signal,
+ sigorsets(set, &current->pending.signal,
&current->signal->shared_pending.signal);
spin_unlock_irq(&current->sighand->siglock);
/* Outside the lock because only this thread touches it. */
- sigandsets(&pending, &current->blocked, &pending);
-
- error = -EFAULT;
- if (!copy_to_user(set, &pending, sigsetsize))
- error = 0;
-
-out:
- return error;
+ sigandsets(set, &current->blocked, set);
+ return 0;
}
/**
@@ -2684,10 +2675,37 @@ 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)
+{
+ sigset_t set;
+ int err = do_sigpending(&set, sigsetsize);
+ if (!err && copy_to_user(uset, &set, sigsetsize))
+ err = -EFAULT;
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPENDING
+COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset,
+ compat_size_t, sigsetsize)
{
- return do_sigpending(set, 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
+#endif
#ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER
@@ -3216,7 +3234,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