| From f72b99c4e8f7637ad38af8bb33e13c7a4b47dfc0 Mon Sep 17 00:00:00 2001 |
| From: Will Deacon <will.deacon@arm.com> |
| Date: Mon, 8 Apr 2019 12:45:09 +0100 |
| Subject: arm64: futex: Fix FUTEX_WAKE_OP atomic ops with non-zero result value |
| |
| [ Upstream commit 84ff7a09c371bc7417eabfda19bf7f113ec917b6 ] |
| |
| Rather embarrassingly, our futex() FUTEX_WAKE_OP implementation doesn't |
| explicitly set the return value on the non-faulting path and instead |
| leaves it holding the result of the underlying atomic operation. This |
| means that any FUTEX_WAKE_OP atomic operation which computes a non-zero |
| value will be reported as having failed. Regrettably, I wrote the buggy |
| code back in 2011 and it was upstreamed as part of the initial arm64 |
| support in 2012. |
| |
| The reasons we appear to get away with this are: |
| |
| 1. FUTEX_WAKE_OP is rarely used and therefore doesn't appear to get |
| exercised by futex() test applications |
| |
| 2. If the result of the atomic operation is zero, the system call |
| behaves correctly |
| |
| 3. Prior to version 2.25, the only operation used by GLIBC set the |
| futex to zero, and therefore worked as expected. From 2.25 onwards, |
| FUTEX_WAKE_OP is not used by GLIBC at all. |
| |
| Fix the implementation by ensuring that the return value is either 0 |
| to indicate that the atomic operation completed successfully, or -EFAULT |
| if we encountered a fault when accessing the user mapping. |
| |
| Cc: <stable@kernel.org> |
| Fixes: 6170a97460db ("arm64: Atomic operations") |
| Signed-off-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/arm64/include/asm/futex.h | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h |
| index 6fb2214333a24..2d78ea6932b7b 100644 |
| --- a/arch/arm64/include/asm/futex.h |
| +++ b/arch/arm64/include/asm/futex.h |
| @@ -58,7 +58,7 @@ do { \ |
| static inline int |
| arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *_uaddr) |
| { |
| - int oldval = 0, ret, tmp; |
| + int oldval, ret, tmp; |
| u32 __user *uaddr = __uaccess_mask_ptr(_uaddr); |
| |
| pagefault_disable(); |
| -- |
| 2.20.1 |
| |