[MIPS] Fix sigset_t endianess swapping issues in 32-bit compat code.
authorRalf Baechle <ralf@linux-mips.org>
Tue, 13 Feb 2007 00:05:11 +0000 (00:05 +0000)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 13 Feb 2007 22:40:51 +0000 (22:40 +0000)
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/kernel/linux32.c
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/signal32.c
arch/mips/kernel/signal_n32.c
include/asm-mips/compat-signal.h [new file with mode: 0644]

index ca7ad78f4def1fea27de9142b1ffd17d91900337..fc4dd6c9dd802c8308f16f422b7f93cc15734905 100644 (file)
@@ -39,6 +39,7 @@
 #include <net/sock.h>
 #include <net/scm.h>
 
+#include <asm/compat-signal.h>
 #include <asm/ipc.h>
 #include <asm/sim.h>
 #include <asm/uaccess.h>
@@ -736,3 +737,49 @@ _sys32_clone(nabi_no_regargs struct pt_regs regs)
        return do_fork(clone_flags, newsp, &regs, 0,
                       parent_tidptr, child_tidptr);
 }
+
+/*
+ * Implement the event wait interface for the eventpoll file. It is the kernel
+ * part of the user space epoll_pwait(2).
+ */
+asmlinkage long compat_sys_epoll_pwait(int epfd,
+       struct epoll_event __user *events, int maxevents, int timeout,
+       const compat_sigset_t __user *sigmask, size_t sigsetsize)
+{
+       int error;
+       sigset_t ksigmask, sigsaved;
+
+       /*
+        * If the caller wants a certain signal mask to be set during the wait,
+        * we apply it here.
+        */
+       if (sigmask) {
+               if (sigsetsize != sizeof(sigset_t))
+                       return -EINVAL;
+               if (!access_ok(VERIFY_READ, sigmask, sizeof(ksigmask)))
+                       return -EFAULT;
+               if (__copy_conv_sigset_from_user(&ksigmask, sigmask))
+                       return -EFAULT;
+               sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP));
+               sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved);
+       }
+
+       error = sys_epoll_wait(epfd, events, maxevents, timeout);
+
+       /*
+        * If we changed the signal mask, we need to restore the original one.
+        * In case we've got a signal while waiting, we do not restore the
+        * signal mask yet, and we allow do_signal() to deliver the signal on
+        * the way back to userspace, before the signal mask is restored.
+        */
+       if (sigmask) {
+               if (error == -EINTR) {
+                       memcpy(&current->saved_sigmask, &sigsaved,
+                               sizeof(sigsaved));
+                       set_thread_flag(TIF_RESTORE_SIGMASK);
+               } else
+                       sigprocmask(SIG_SETMASK, &sigsaved, NULL);
+       }
+
+       return error;
+}
index e569b846e9a380ff96e65507f6e13ec3d5f6e414..10e9a18630aa47b168c8140b99f80875e5c76c32 100644 (file)
@@ -470,4 +470,4 @@ sys_call_table:
        PTR     sys_get_robust_list
        PTR     sys_kexec_load                  /* 5270 */
        PTR     sys_getcpu
-       PTR     sys_epoll_pwait
+       PTR     compat_sys_epoll_pwait
index ee8802b597582ef1a71458dfeb93ad0de5412a74..2ceda4644a4d7b4ee64d778753b8fcd0c9dce170 100644 (file)
@@ -396,4 +396,4 @@ EXPORT(sysn32_call_table)
        PTR     compat_sys_get_robust_list
        PTR     compat_sys_kexec_load
        PTR     sys_getcpu
-       PTR     sys_epoll_pwait
+       PTR     compat_sys_epoll_pwait
index db00c333bff3a3d3f027db645047f0003a87fb72..c28cb21514c858dc856918f22211f4036565b7c7 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
  */
 #include <linux/cache.h>
+#include <linux/compat.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
@@ -24,6 +25,7 @@
 
 #include <asm/abi.h>
 #include <asm/asm.h>
+#include <asm/compat-signal.h>
 #include <linux/bitops.h>
 #include <asm/cacheflush.h>
 #include <asm/sim.h>
@@ -517,7 +519,7 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
        frame = (struct sigframe32 __user *) regs.regs[29];
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
-       if (__copy_from_user(&blocked, &frame->sf_mask, sizeof(blocked)))
+       if (__copy_conv_sigset_from_user(&blocked, &frame->sf_mask))
                goto badframe;
 
        sigdelsetmask(&blocked, ~_BLOCKABLE);
@@ -554,7 +556,7 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        frame = (struct rt_sigframe32 __user *) regs.regs[29];
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
-       if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
+       if (__copy_conv_sigset_from_user(&set, &frame->rs_uc.uc_sigmask))
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
@@ -609,7 +611,8 @@ int setup_frame_32(struct k_sigaction * ka, struct pt_regs *regs,
        err |= install_sigtramp(frame->sf_code, __NR_O32_sigreturn);
 
        err |= setup_sigcontext32(regs, &frame->sf_sc);
-       err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set));
+       err |= __copy_conv_sigset_to_user(&frame->sf_mask, set);
+
        if (err)
                goto give_sigsegv;
 
@@ -668,7 +671,7 @@ int setup_rt_frame_32(struct k_sigaction * ka, struct pt_regs *regs,
        err |= __put_user(current->sas_ss_size,
                          &frame->rs_uc.uc_stack.ss_size);
        err |= setup_sigcontext32(regs, &frame->rs_uc.uc_mcontext);
-       err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
+       err |= __copy_conv_sigset_to_user(&frame->rs_uc.uc_sigmask, set);
 
        if (err)
                goto give_sigsegv;
index 1a5f248faf3b193f130a60e5cd81680df0072069..7ca2a078841fb65253d5e9aeec70b8dfcebbd5c9 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <asm/asm.h>
 #include <asm/cacheflush.h>
+#include <asm/compat-signal.h>
 #include <asm/sim.h>
 #include <asm/uaccess.h>
 #include <asm/ucontext.h>
@@ -63,7 +64,7 @@ struct ucontextn32 {
        s32                 uc_link;
        stack32_t           uc_stack;
        struct sigcontext   uc_mcontext;
-       sigset_t            uc_sigmask;   /* mask last for extensibility */
+       compat_sigset_t     uc_sigmask;   /* mask last for extensibility */
 };
 
 #if ICACHE_REFILLS_WORKAROUND_WAR == 0
@@ -129,7 +130,7 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        frame = (struct rt_sigframe_n32 __user *) regs.regs[29];
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
-       if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
+       if (__copy_conv_sigset_from_user(&set, &frame->rs_uc.uc_sigmask))
                goto badframe;
 
        sigdelsetmask(&set, ~_BLOCKABLE);
@@ -195,7 +196,7 @@ int setup_rt_frame_n32(struct k_sigaction * ka,
        err |= __put_user(current->sas_ss_size,
                          &frame->rs_uc.uc_stack.ss_size);
        err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext);
-       err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
+       err |= __copy_conv_sigset_to_user(&frame->rs_uc.uc_sigmask, set);
 
        if (err)
                goto give_sigsegv;
diff --git a/include/asm-mips/compat-signal.h b/include/asm-mips/compat-signal.h
new file mode 100644 (file)
index 0000000..6720770
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __ASM_COMPAT_SIGNAL_H
+#define __ASM_COMPAT_SIGNAL_H
+
+#include <linux/bug.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+
+static inline int __copy_conv_sigset_to_user(compat_sigset_t __user *d,
+       const sigset_t *s)
+{
+       int err;
+
+       BUG_ON(sizeof(*d) != sizeof(*s));
+       BUG_ON(_NSIG_WORDS != 2);
+
+       err  = __put_user(s->sig[0],       &d->sig[0]);
+       err |= __put_user(s->sig[0] >> 32, &d->sig[1]);
+       err |= __put_user(s->sig[1],       &d->sig[2]);
+       err |= __put_user(s->sig[1] >> 32, &d->sig[3]);
+
+       return err;
+}
+
+static inline int __copy_conv_sigset_from_user(sigset_t *d,
+       const compat_sigset_t __user *s)
+{
+       int err;
+       union sigset_u {
+               sigset_t        s;
+               compat_sigset_t c;
+       } *u = (union sigset_u *) d;
+
+       BUG_ON(sizeof(*d) != sizeof(*s));
+       BUG_ON(_NSIG_WORDS != 2);
+
+       if (unlikely(!access_ok(VERIFY_READ, d, sizeof(*d))))
+               return -EFAULT;
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+       err  = __get_user(u->c.sig[1], &s->sig[0]);
+       err |= __get_user(u->c.sig[0], &s->sig[1]);
+       err |= __get_user(u->c.sig[3], &s->sig[2]);
+       err |= __get_user(u->c.sig[2], &s->sig[3]);
+#endif
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+       err  = __get_user(u->c.sig[0], &s->sig[0]);
+       err |= __get_user(u->c.sig[1], &s->sig[1]);
+       err |= __get_user(u->c.sig[2], &s->sig[2]);
+       err |= __get_user(u->c.sig[3], &s->sig[3]);
+#endif
+
+       return err;
+}
+
+#endif /* __ASM_COMPAT_SIGNAL_H */