Ivan T. Ivanov 60a154
From: Will Deacon <will.deacon@arm.com>
Ivan T. Ivanov 60a154
Date: Mon, 8 Apr 2019 12:45:09 +0100
Ivan T. Ivanov 60a154
Subject: arm64: futex: Fix FUTEX_WAKE_OP atomic ops with non-zero result value
Ivan T. Ivanov 60a154
Git-commit: 045afc24124d80c6998d9c770844c67912083506
Ivan T. Ivanov 60a154
Patch-mainline: v5.1-rc5
Ivan T. Ivanov 60a154
References: git-fixes
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
Rather embarrassingly, our futex() FUTEX_WAKE_OP implementation doesn't
Ivan T. Ivanov 60a154
explicitly set the return value on the non-faulting path and instead
Ivan T. Ivanov 60a154
leaves it holding the result of the underlying atomic operation. This
Ivan T. Ivanov 60a154
means that any FUTEX_WAKE_OP atomic operation which computes a non-zero
Ivan T. Ivanov 60a154
value will be reported as having failed. Regrettably, I wrote the buggy
Ivan T. Ivanov 60a154
code back in 2011 and it was upstreamed as part of the initial arm64
Ivan T. Ivanov 60a154
support in 2012.
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
The reasons we appear to get away with this are:
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
  1. FUTEX_WAKE_OP is rarely used and therefore doesn't appear to get
Ivan T. Ivanov 60a154
     exercised by futex() test applications
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
  2. If the result of the atomic operation is zero, the system call
Ivan T. Ivanov 60a154
     behaves correctly
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
  3. Prior to version 2.25, the only operation used by GLIBC set the
Ivan T. Ivanov 60a154
     futex to zero, and therefore worked as expected. From 2.25 onwards,
Ivan T. Ivanov 60a154
     FUTEX_WAKE_OP is not used by GLIBC at all.
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
Fix the implementation by ensuring that the return value is either 0
Ivan T. Ivanov 60a154
to indicate that the atomic operation completed successfully, or -EFAULT
Ivan T. Ivanov 60a154
if we encountered a fault when accessing the user mapping.
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
Cc: <stable@kernel.org>
Ivan T. Ivanov 60a154
Fixes: 6170a97460db ("arm64: Atomic operations")
Ivan T. Ivanov 60a154
Signed-off-by: Will Deacon <will.deacon@arm.com>
Ivan T. Ivanov 60a154
Acked-by: Ivan T. Ivanov <iivanov@suse.de>
Ivan T. Ivanov 60a154
---
Ivan T. Ivanov 60a154
 arch/arm64/include/asm/futex.h |   16 ++++++++--------
Ivan T. Ivanov 60a154
 1 file changed, 8 insertions(+), 8 deletions(-)
Ivan T. Ivanov 60a154
Ivan T. Ivanov 60a154
--- a/arch/arm64/include/asm/futex.h
Ivan T. Ivanov 60a154
+++ b/arch/arm64/include/asm/futex.h
Ivan T. Ivanov 60a154
@@ -30,8 +30,8 @@ do {									\
Ivan T. Ivanov 60a154
 "	prfm	pstl1strm, %2\n"					\
Ivan T. Ivanov 60a154
 "1:	ldxr	%w1, %2\n"						\
Ivan T. Ivanov 60a154
 	insn "\n"							\
Ivan T. Ivanov 60a154
-"2:	stlxr	%w3, %w0, %2\n"						\
Ivan T. Ivanov 60a154
-"	cbnz	%w3, 1b\n"						\
Ivan T. Ivanov 60a154
+"2:	stlxr	%w0, %w3, %2\n"						\
Ivan T. Ivanov 60a154
+"	cbnz	%w0, 1b\n"						\
Ivan T. Ivanov 60a154
 "	dmb	ish\n"							\
Ivan T. Ivanov 60a154
 "3:\n"									\
Ivan T. Ivanov 60a154
 "	.pushsection .fixup,\"ax\"\n"					\
Ivan T. Ivanov 60a154
@@ -50,30 +50,30 @@ do {									\
Ivan T. Ivanov 60a154
 static inline int
Ivan T. Ivanov 60a154
 arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr)
Ivan T. Ivanov 60a154
 {
Ivan T. Ivanov 60a154
-	int oldval = 0, ret, tmp;
Ivan T. Ivanov 60a154
+	int oldval, ret, tmp;
Ivan T. Ivanov 60a154
 	u32 __user *uaddr = __uaccess_mask_ptr(_uaddr);
Ivan T. Ivanov 60a154
 
Ivan T. Ivanov 60a154
 	pagefault_disable();
Ivan T. Ivanov 60a154
 
Ivan T. Ivanov 60a154
 	switch (op) {
Ivan T. Ivanov 60a154
 	case FUTEX_OP_SET:
Ivan T. Ivanov 60a154
-		__futex_atomic_op("mov	%w0, %w4",
Ivan T. Ivanov 60a154
+		__futex_atomic_op("mov	%w3, %w4",
Ivan T. Ivanov 60a154
 				  ret, oldval, uaddr, tmp, oparg);
Ivan T. Ivanov 60a154
 		break;
Ivan T. Ivanov 60a154
 	case FUTEX_OP_ADD:
Ivan T. Ivanov 60a154
-		__futex_atomic_op("add	%w0, %w1, %w4",
Ivan T. Ivanov 60a154
+		__futex_atomic_op("add	%w3, %w1, %w4",
Ivan T. Ivanov 60a154
 				  ret, oldval, uaddr, tmp, oparg);
Ivan T. Ivanov 60a154
 		break;
Ivan T. Ivanov 60a154
 	case FUTEX_OP_OR:
Ivan T. Ivanov 60a154
-		__futex_atomic_op("orr	%w0, %w1, %w4",
Ivan T. Ivanov 60a154
+		__futex_atomic_op("orr	%w3, %w1, %w4",
Ivan T. Ivanov 60a154
 				  ret, oldval, uaddr, tmp, oparg);
Ivan T. Ivanov 60a154
 		break;
Ivan T. Ivanov 60a154
 	case FUTEX_OP_ANDN:
Ivan T. Ivanov 60a154
-		__futex_atomic_op("and	%w0, %w1, %w4",
Ivan T. Ivanov 60a154
+		__futex_atomic_op("and	%w3, %w1, %w4",
Ivan T. Ivanov 60a154
 				  ret, oldval, uaddr, tmp, ~oparg);
Ivan T. Ivanov 60a154
 		break;
Ivan T. Ivanov 60a154
 	case FUTEX_OP_XOR:
Ivan T. Ivanov 60a154
-		__futex_atomic_op("eor	%w0, %w1, %w4",
Ivan T. Ivanov 60a154
+		__futex_atomic_op("eor	%w3, %w1, %w4",
Ivan T. Ivanov 60a154
 				  ret, oldval, uaddr, tmp, oparg);
Ivan T. Ivanov 60a154
 		break;
Ivan T. Ivanov 60a154
 	default: