powerpc: fix swapcontext backwards compat. with VSX ucontext changes
authorMichael Neuling <mikey@neuling.org>
Tue, 8 Jul 2008 08:43:41 +0000 (18:43 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Wed, 9 Jul 2008 06:30:47 +0000 (16:30 +1000)
When the ucontext changed to add the VSX context, this broke backwards
compatibly on swapcontext.  swapcontext only compares the ucontext size
passed in from the user to the new kernel ucontext size.

This adds a check against the old ucontext size (with VMX but without
VSX).  It also adds some sanity check for ucontexts without VSX, but
where VSX is used according the MSR.  Fixes for both 32 and 64bit
processes on 64bit kernels

Kudos to Paulus for noticing.

Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/signal_32.c
arch/powerpc/kernel/signal_64.c

index 9991e2a58bf41fb6e561ba122b917a45f03a93d6..6f6810db0a74eb47ddbdae23d264529c7fb68f63 100644 (file)
 #define mcontext       mcontext32
 #define ucontext       ucontext32
 
+/*
+ * Userspace code may pass a ucontext which doesn't include VSX added
+ * at the end.  We need to check for this case.
+ */
+#define UCONTEXTSIZEWITHOUTVSX \
+               (sizeof(struct ucontext) - sizeof(elf_vsrreghalf_t32))
+
 /*
  * Returning 0 means we return to userspace via
  * ret_from_except and thus restore all user
@@ -930,12 +937,42 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
 {
        unsigned char tmp;
 
+#ifdef CONFIG_PPC64
+       unsigned long new_msr = 0;
+
+       if (new_ctx &&
+           __get_user(new_msr, &new_ctx->uc_mcontext.mc_gregs[PT_MSR]))
+               return -EFAULT;
+       /*
+        * Check that the context is not smaller than the original
+        * size (with VMX but without VSX)
+        */
+       if (ctx_size < UCONTEXTSIZEWITHOUTVSX)
+               return -EINVAL;
+       /*
+        * If the new context state sets the MSR VSX bits but
+        * it doesn't provide VSX state.
+        */
+       if ((ctx_size < sizeof(struct ucontext)) &&
+           (new_msr & MSR_VSX))
+               return -EINVAL;
+#ifdef CONFIG_VSX
+       /*
+        * If userspace doesn't provide enough room for VSX data,
+        * but current thread has used VSX, we don't have anywhere
+        * to store the full context back into.
+        */
+       if ((ctx_size < sizeof(struct ucontext)) &&
+           (current->thread.used_vsr && old_ctx))
+               return -EINVAL;
+#endif
+#else
        /* Context size is for future use. Right now, we only make sure
         * we are passed something we understand
         */
        if (ctx_size < sizeof(struct ucontext))
                return -EINVAL;
-
+#endif
        if (old_ctx != NULL) {
                struct mcontext __user *mctx;
 
index 93ebfb6944b6e9ff5e9a49e1c6ae039f582857ca..5f9d2ef2e24be8668cb0b4562ca332971796d7b7 100644 (file)
@@ -267,6 +267,13 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp)
        return err;
 }
 
+/*
+ * Userspace code may pass a ucontext which doesn't include VSX added
+ * at the end.  We need to check for this case.
+ */
+#define UCONTEXTSIZEWITHOUTVSX \
+               (sizeof(struct ucontext) - 32*sizeof(long))
+
 /*
  * Handle {get,set,swap}_context operations
  */
@@ -276,13 +283,34 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
 {
        unsigned char tmp;
        sigset_t set;
+       unsigned long new_msr = 0;
 
-       /* Context size is for future use. Right now, we only make sure
-        * we are passed something we understand
+       if (new_ctx &&
+           __get_user(new_msr, &new_ctx->uc_mcontext.gp_regs[PT_MSR]))
+               return -EFAULT;
+       /*
+        * Check that the context is not smaller than the original
+        * size (with VMX but without VSX)
         */
-       if (ctx_size < sizeof(struct ucontext))
+       if (ctx_size < UCONTEXTSIZEWITHOUTVSX)
                return -EINVAL;
-
+       /*
+        * If the new context state sets the MSR VSX bits but
+        * it doesn't provide VSX state.
+        */
+       if ((ctx_size < sizeof(struct ucontext)) &&
+           (new_msr & MSR_VSX))
+               return -EINVAL;
+#ifdef CONFIG_VSX
+       /*
+        * If userspace doesn't provide enough room for VSX data,
+        * but current thread has used VSX, we don't have anywhere
+        * to store the full context back into.
+        */
+       if ((ctx_size < sizeof(struct ucontext)) &&
+           (current->thread.used_vsr && old_ctx))
+               return -EINVAL;
+#endif
        if (old_ctx != NULL) {
                if (!access_ok(VERIFY_WRITE, old_ctx, sizeof(*old_ctx))
                    || setup_sigcontext(&old_ctx->uc_mcontext, regs, 0, NULL, 0)