]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
futex: Remove duplicated code and fix undefined behaviour
authorJiri Slaby <jslaby@suse.cz>
Thu, 24 Aug 2017 07:31:05 +0000 (09:31 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 19 May 2018 08:27:00 +0000 (10:27 +0200)
commit 30d6e0a4190d37740e9447e4e4815f06992dd8c3 upstream.

There is code duplicated over all architecture's headers for
futex_atomic_op_inuser. Namely op decoding, access_ok check for uaddr,
and comparison of the result.

Remove this duplication and leave up to the arches only the needed
assembly which is now in arch_futex_atomic_op_inuser.

This effectively distributes the Will Deacon's arm64 fix for undefined
behaviour reported by UBSAN to all architectures. The fix was done in
commit 5f16a046f8e1 (arm64: futex: Fix undefined behaviour with
FUTEX_OP_OPARG_SHIFT usage). Look there for an example dump.

And as suggested by Thomas, check for negative oparg too, because it was
also reported to cause undefined behaviour report.

Note that s390 removed access_ok check in d12a29703 ("s390/uaccess:
remove pointless access_ok() checks") as access_ok there returns true.
We introduce it back to the helper for the sake of simplicity (it gets
optimized away anyway).

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com> [s390]
Acked-by: Chris Metcalf <cmetcalf@mellanox.com> [for tile]
Reviewed-by: Darren Hart (VMware) <dvhart@infradead.org>
Reviewed-by: Will Deacon <will.deacon@arm.com> [core/arm64]
Cc: linux-mips@linux-mips.org
Cc: Rich Felker <dalias@libc.org>
Cc: linux-ia64@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: peterz@infradead.org
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: sparclinux@vger.kernel.org
Cc: Jonas Bonn <jonas@southpole.se>
Cc: linux-s390@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: linux-hexagon@vger.kernel.org
Cc: Helge Deller <deller@gmx.de>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Matt Turner <mattst88@gmail.com>
Cc: linux-snps-arc@lists.infradead.org
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-xtensa@linux-xtensa.org
Cc: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
Cc: openrisc@lists.librecores.org
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Stafford Horne <shorne@gmail.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Richard Henderson <rth@twiddle.net>
Cc: Chris Zankel <chris@zankel.net>
Cc: Michal Simek <monstr@monstr.eu>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-parisc@vger.kernel.org
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Richard Kuo <rkuo@codeaurora.org>
Cc: linux-alpha@vger.kernel.org
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: "David S. Miller" <davem@davemloft.net>
Link: http://lkml.kernel.org/r/20170824073105.3901-1-jslaby@suse.cz
Cc: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
20 files changed:
arch/alpha/include/asm/futex.h
arch/arc/include/asm/futex.h
arch/arm/include/asm/futex.h
arch/arm64/include/asm/futex.h
arch/frv/include/asm/futex.h
arch/frv/kernel/futex.c
arch/hexagon/include/asm/futex.h
arch/ia64/include/asm/futex.h
arch/microblaze/include/asm/futex.h
arch/mips/include/asm/futex.h
arch/parisc/include/asm/futex.h
arch/powerpc/include/asm/futex.h
arch/s390/include/asm/futex.h
arch/sh/include/asm/futex.h
arch/sparc/include/asm/futex_64.h
arch/tile/include/asm/futex.h
arch/x86/include/asm/futex.h
arch/xtensa/include/asm/futex.h
include/asm-generic/futex.h
kernel/futex.c

index f939794363ac6f94ad82a192ede6b9410f7bc002..56474690e6855d0ffd54fb9cd69258badde213ca 100644 (file)
        :       "r" (uaddr), "r"(oparg)                         \
        :       "memory")
 
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -66,17 +58,9 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 11e1b1f3acda7fb0a741dcf2d3aed5ae55d79de1..eb887dd13e74862b9bfbba21c2d2d9f09e3b113c 100644 (file)
 
 #endif
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
-               return -EFAULT;
-
 #ifndef CONFIG_ARC_HAS_LLSC
        preempt_disable();      /* to guarantee atomic r-m-w of futex op */
 #endif
@@ -118,30 +109,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
        preempt_enable();
 #endif
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ:
-                       ret = (oldval == cmparg);
-                       break;
-               case FUTEX_OP_CMP_NE:
-                       ret = (oldval != cmparg);
-                       break;
-               case FUTEX_OP_CMP_LT:
-                       ret = (oldval < cmparg);
-                       break;
-               case FUTEX_OP_CMP_GE:
-                       ret = (oldval >= cmparg);
-                       break;
-               case FUTEX_OP_CMP_LE:
-                       ret = (oldval <= cmparg);
-                       break;
-               case FUTEX_OP_CMP_GT:
-                       ret = (oldval > cmparg);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 6795368ad0238068c55fa4c8b56e0d67d9b9a5cf..cc414382dab4b47613619293a0e6e9033e480ec6 100644 (file)
@@ -128,20 +128,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 #endif /* !SMP */
 
 static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret, tmp;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
-
 #ifndef CONFIG_SMP
        preempt_disable();
 #endif
@@ -172,17 +162,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
        preempt_enable();
 #endif
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 4e5f36a804b49d5a2c5b9e842151179b9784a8f7..2a5090fb9113f5d876f6bc0fe8f20504ba803f48 100644 (file)
        : "memory")
 
 static inline int
-futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *_uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (int)(encoded_op << 8) >> 20;
-       int cmparg = (int)(encoded_op << 20) >> 20;
        int oldval = 0, ret, tmp;
-       u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
-
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1U << (oparg & 0x1f);
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -95,17 +84,9 @@ futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *_uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 4bea27f50a7ab4f72d8cbb28cecf181c89e9cf28..2702bd802d447852d7c19ba805f0a2558270d767 100644 (file)
@@ -7,7 +7,8 @@
 #include <asm/errno.h>
 #include <asm/uaccess.h>
 
-extern int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr);
+extern int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr);
 
 static inline int
 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
index d155ca9e5098c8963f6ea5b0d8c987a6963f0538..37f7b2bf7f730c092d3ea15806c9163c8a3019d9 100644 (file)
@@ -186,20 +186,10 @@ static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, int *_o
 /*
  * do the futex operations
  */
-int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
-
        pagefault_disable();
 
        switch (op) {
@@ -225,18 +215,9 @@ int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS; break;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
 
        return ret;
 
-} /* end futex_atomic_op_inuser() */
+} /* end arch_futex_atomic_op_inuser() */
index 7e597f8434da0c13f3d0e68c6b3d838b0d0126d5..c607b77c8215d2995e2f30f1df5bcf497bd1e65b 100644 (file)
 
 
 static inline int
-futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -72,30 +63,9 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ:
-                       ret = (oldval == cmparg);
-                       break;
-               case FUTEX_OP_CMP_NE:
-                       ret = (oldval != cmparg);
-                       break;
-               case FUTEX_OP_CMP_LT:
-                       ret = (oldval < cmparg);
-                       break;
-               case FUTEX_OP_CMP_GE:
-                       ret = (oldval >= cmparg);
-                       break;
-               case FUTEX_OP_CMP_LE:
-                       ret = (oldval <= cmparg);
-                       break;
-               case FUTEX_OP_CMP_GT:
-                       ret = (oldval > cmparg);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 76acbcd5c06090e25cd35e77b3afbd5c16f7c0ae..6d67dc1eaf2be247815629184f68da72050b61eb 100644 (file)
@@ -45,18 +45,9 @@ do {                                                                 \
 } while (0)
 
 static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -84,17 +75,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 01848f056f439d251f9f61dcc0806a8d47aad4ba..a9dad9e5e132f1c0f3aa3df6af6efdfba8edf1a6 100644 (file)
 })
 
 static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -66,30 +57,9 @@ futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ:
-                       ret = (oldval == cmparg);
-                       break;
-               case FUTEX_OP_CMP_NE:
-                       ret = (oldval != cmparg);
-                       break;
-               case FUTEX_OP_CMP_LT:
-                       ret = (oldval < cmparg);
-                       break;
-               case FUTEX_OP_CMP_GE:
-                       ret = (oldval >= cmparg);
-                       break;
-               case FUTEX_OP_CMP_LE:
-                       ret = (oldval <= cmparg);
-                       break;
-               case FUTEX_OP_CMP_GT:
-                       ret = (oldval > cmparg);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 1de190bdfb9c9fef90b8976503242e09b97e7f60..a9e61ea54ca96ba69fa49f009d4fc98ff9cb1bdf 100644 (file)
 }
 
 static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -125,17 +116,9 @@ futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index ac8bd586ace8d17329e2536244b7833c0db46c83..06a1a883c72f5a9b50270aee2d271a4065fb1d22 100644 (file)
@@ -32,22 +32,12 @@ _futex_spin_unlock_irqrestore(u32 __user *uaddr, unsigned long int *flags)
 }
 
 static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
 {
        unsigned long int flags;
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval, ret;
        u32 tmp;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
-               return -EFAULT;
-
        _futex_spin_lock_irqsave(uaddr, &flags);
        pagefault_disable();
 
@@ -85,17 +75,9 @@ out_pagefault_enable:
        pagefault_enable();
        _futex_spin_unlock_irqrestore(uaddr, &flags);
 
-       if (ret == 0) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 2a9cf845473bb51a4aea7d5317dde0a4b5ab37ed..f4c7467f74655458e3f3269f3aa9a630cf99f91e 100644 (file)
        : "b" (uaddr), "i" (-EFAULT), "r" (oparg) \
        : "cr0", "memory")
 
-static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -68,17 +60,9 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index a4811aa0304d03264c4e63626d61619c62d16f0c..8f8eec9e1198cc97fef9fd036a953dbd7c9d6009 100644 (file)
                : "0" (-EFAULT), "d" (oparg), "a" (uaddr),              \
                  "m" (*uaddr) : "cc");
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, newval, ret;
 
        load_kernel_asce();
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
 
        pagefault_disable();
        switch (op) {
@@ -60,17 +55,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
        }
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index d0078747d308cad1e98d28970f2593a2ef6ad752..8f8cf941a8cd29c716a923002490dc5506ee57a5 100644 (file)
@@ -27,21 +27,12 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
        return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval);
 }
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       u32 oparg = (encoded_op << 8) >> 20;
-       u32 cmparg = (encoded_op << 20) >> 20;
        u32 oldval, newval, prev;
        int ret;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
-
        pagefault_disable();
 
        do {
@@ -80,17 +71,8 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = ((int)oldval < (int)cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = ((int)oldval >= (int)cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = ((int)oldval <= (int)cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = ((int)oldval > (int)cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
 
        return ret;
 }
index 4e899b0dabf79154f0ccc96a2ed1e0d7d230e04b..1cfd89d922080103d8e4b55c8cc36d94290a75df 100644 (file)
        : "r" (uaddr), "r" (oparg), "i" (-EFAULT)       \
        : "memory")
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret, tem;
 
-       if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))))
-               return -EFAULT;
        if (unlikely((((unsigned long) uaddr) & 0x3UL)))
                return -EINVAL;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
        pagefault_disable();
 
        switch (op) {
@@ -69,17 +61,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index e64a1b75fc386c4786d6a4988a4f5eb71bbfbd27..83c1e639b41111049aad89164067d4dd8b587bb5 100644 (file)
        lock = __atomic_hashed_lock((int __force *)uaddr)
 #endif
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int uninitialized_var(val), ret;
 
        __futex_prolog();
@@ -119,12 +116,6 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
        /* The 32-bit futex code makes this assumption, so validate it here. */
        BUILD_BUG_ON(sizeof(atomic_t) != sizeof(int));
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
-
        pagefault_disable();
        switch (op) {
        case FUTEX_OP_SET:
@@ -148,30 +139,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
        }
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ:
-                       ret = (val == cmparg);
-                       break;
-               case FUTEX_OP_CMP_NE:
-                       ret = (val != cmparg);
-                       break;
-               case FUTEX_OP_CMP_LT:
-                       ret = (val < cmparg);
-                       break;
-               case FUTEX_OP_CMP_GE:
-                       ret = (val >= cmparg);
-                       break;
-               case FUTEX_OP_CMP_LE:
-                       ret = (val <= cmparg);
-                       break;
-               case FUTEX_OP_CMP_GT:
-                       ret = (val > cmparg);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = val;
+
        return ret;
 }
 
index b4c1f545343663057460376157de5d9d55a6f528..f4dc9b63bdda9df2b4d79ae12c601ba6b9e0ec0d 100644 (file)
                       "+m" (*uaddr), "=&r" (tem)               \
                     : "r" (oparg), "i" (-EFAULT), "1" (0))
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret, tem;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
-
        pagefault_disable();
 
        switch (op) {
@@ -80,30 +71,9 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ:
-                       ret = (oldval == cmparg);
-                       break;
-               case FUTEX_OP_CMP_NE:
-                       ret = (oldval != cmparg);
-                       break;
-               case FUTEX_OP_CMP_LT:
-                       ret = (oldval < cmparg);
-                       break;
-               case FUTEX_OP_CMP_GE:
-                       ret = (oldval >= cmparg);
-                       break;
-               case FUTEX_OP_CMP_LE:
-                       ret = (oldval <= cmparg);
-                       break;
-               case FUTEX_OP_CMP_GT:
-                       ret = (oldval > cmparg);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index 72bfc1cbc2b546578d902866d76256fffa7451fe..5bfbc1c401d4cfb82fe5596f0a24689cabecdc98 100644 (file)
        : "r" (uaddr), "I" (-EFAULT), "r" (oparg)       \
        : "memory")
 
-static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+static inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
+               u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
 #if !XCHAL_HAVE_S32C1I
        return -ENOSYS;
@@ -89,19 +81,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (ret)
-               return ret;
+       if (!ret)
+               *oval = oldval;
 
-       switch (cmp) {
-       case FUTEX_OP_CMP_EQ: return (oldval == cmparg);
-       case FUTEX_OP_CMP_NE: return (oldval != cmparg);
-       case FUTEX_OP_CMP_LT: return (oldval < cmparg);
-       case FUTEX_OP_CMP_GE: return (oldval >= cmparg);
-       case FUTEX_OP_CMP_LE: return (oldval <= cmparg);
-       case FUTEX_OP_CMP_GT: return (oldval > cmparg);
-       }
-
-       return -ENOSYS;
+       return ret;
 }
 
 static inline int
index bf2d34c9d804334cd0c634bf4d8ed921c08cfd7a..f0d8b1c51343c813761416d147255aeac9e74140 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 /**
- * futex_atomic_op_inuser() - Atomic arithmetic operation with constant
+ * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
  *                       argument and comparison of the previous
  *                       futex value with another constant.
  *
  * <0 - On error
  */
 static inline int
-futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval, ret;
        u32 tmp;
 
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
        preempt_disable();
        pagefault_disable();
 
@@ -74,17 +67,9 @@ out_pagefault_enable:
        pagefault_enable();
        preempt_enable();
 
-       if (ret == 0) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (ret == 0)
+               *oval = oldval;
+
        return ret;
 }
 
@@ -126,18 +111,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
 
 #else
 static inline int
-futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
 {
-       int op = (encoded_op >> 28) & 7;
-       int cmp = (encoded_op >> 24) & 15;
-       int oparg = (encoded_op << 8) >> 20;
-       int cmparg = (encoded_op << 20) >> 20;
        int oldval = 0, ret;
-       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
-               oparg = 1 << oparg;
-
-       if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
-               return -EFAULT;
 
        pagefault_disable();
 
@@ -153,17 +129,9 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 
        pagefault_enable();
 
-       if (!ret) {
-               switch (cmp) {
-               case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
-               case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
-               case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
-               case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
-               case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
-               case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
-               default: ret = -ENOSYS;
-               }
-       }
+       if (!ret)
+               *oval = oldval;
+
        return ret;
 }
 
index bb2265ae5cbcad9ce2a1c7d5c423677c22a32249..401d99a4e3cb40b1836a800424eb357c7fdb1f4d 100644 (file)
@@ -1458,6 +1458,45 @@ out:
        return ret;
 }
 
+static int futex_atomic_op_inuser(unsigned int encoded_op, u32 __user *uaddr)
+{
+       unsigned int op =         (encoded_op & 0x70000000) >> 28;
+       unsigned int cmp =        (encoded_op & 0x0f000000) >> 24;
+       int oparg = sign_extend32((encoded_op & 0x00fff000) >> 12, 12);
+       int cmparg = sign_extend32(encoded_op & 0x00000fff, 12);
+       int oldval, ret;
+
+       if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) {
+               if (oparg < 0 || oparg > 31)
+                       return -EINVAL;
+               oparg = 1 << oparg;
+       }
+
+       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+               return -EFAULT;
+
+       ret = arch_futex_atomic_op_inuser(op, oparg, &oldval, uaddr);
+       if (ret)
+               return ret;
+
+       switch (cmp) {
+       case FUTEX_OP_CMP_EQ:
+               return oldval == cmparg;
+       case FUTEX_OP_CMP_NE:
+               return oldval != cmparg;
+       case FUTEX_OP_CMP_LT:
+               return oldval < cmparg;
+       case FUTEX_OP_CMP_GE:
+               return oldval >= cmparg;
+       case FUTEX_OP_CMP_LE:
+               return oldval <= cmparg;
+       case FUTEX_OP_CMP_GT:
+               return oldval > cmparg;
+       default:
+               return -ENOSYS;
+       }
+}
+
 /*
  * Wake up all waiters hashed on the physical page that is mapped
  * to this virtual address: