Commit 1c5b51df authored by Will Deacon's avatar Will Deacon

arm64: swp emulation: bound LL/SC retries before rescheduling

If a CPU does not implement a global monitor for certain memory types,
then userspace can attempt a kernel DoS by issuing SWP instructions
targetting the problematic memory (for example, a framebuffer mapped
with non-cacheable attributes).

The SWP emulation code protects against these sorts of attacks by
checking for pending signals and potentially rescheduling when the STXR
instruction fails during the emulation. Whilst this is good for avoiding
livelock, it harms emulation of legitimate SWP instructions on CPUs
where forward progress is not guaranteed if there are memory accesses to
the same reservation granule (up to 2k) between the failing STXR and
the retry of the LDXR.

This patch solves the problem by retrying the STXR a bounded number of
times (4) before breaking out of the LL/SC loop and looking for
something else to do.

Cc: <stable@vger.kernel.org>
Fixes: bd35a4ad ("arm64: Port SWP/SWPB emulation support from arm")
Reviewed-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 91cb163e
...@@ -280,35 +280,43 @@ static void __init register_insn_emulation_sysctl(struct ctl_table *table) ...@@ -280,35 +280,43 @@ static void __init register_insn_emulation_sysctl(struct ctl_table *table)
/* /*
* Error-checking SWP macros implemented using ldxr{b}/stxr{b} * Error-checking SWP macros implemented using ldxr{b}/stxr{b}
*/ */
#define __user_swpX_asm(data, addr, res, temp, B) \
/* Arbitrary constant to ensure forward-progress of the LL/SC loop */
#define __SWP_LL_SC_LOOPS 4
#define __user_swpX_asm(data, addr, res, temp, temp2, B) \
__asm__ __volatile__( \ __asm__ __volatile__( \
" mov %w3, %w7\n" \
ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \ ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
CONFIG_ARM64_PAN) \ CONFIG_ARM64_PAN) \
"0: ldxr"B" %w2, [%3]\n" \ "0: ldxr"B" %w2, [%4]\n" \
"1: stxr"B" %w0, %w1, [%3]\n" \ "1: stxr"B" %w0, %w1, [%4]\n" \
" cbz %w0, 2f\n" \ " cbz %w0, 2f\n" \
" mov %w0, %w4\n" \ " sub %w3, %w3, #1\n" \
" cbnz %w3, 0b\n" \
" mov %w0, %w5\n" \
" b 3f\n" \ " b 3f\n" \
"2:\n" \ "2:\n" \
" mov %w1, %w2\n" \ " mov %w1, %w2\n" \
"3:\n" \ "3:\n" \
" .pushsection .fixup,\"ax\"\n" \ " .pushsection .fixup,\"ax\"\n" \
" .align 2\n" \ " .align 2\n" \
"4: mov %w0, %w5\n" \ "4: mov %w0, %w6\n" \
" b 3b\n" \ " b 3b\n" \
" .popsection" \ " .popsection" \
_ASM_EXTABLE(0b, 4b) \ _ASM_EXTABLE(0b, 4b) \
_ASM_EXTABLE(1b, 4b) \ _ASM_EXTABLE(1b, 4b) \
ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \ ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
CONFIG_ARM64_PAN) \ CONFIG_ARM64_PAN) \
: "=&r" (res), "+r" (data), "=&r" (temp) \ : "=&r" (res), "+r" (data), "=&r" (temp), "=&r" (temp2) \
: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT), \
"i" (__SWP_LL_SC_LOOPS) \
: "memory") : "memory")
#define __user_swp_asm(data, addr, res, temp) \ #define __user_swp_asm(data, addr, res, temp, temp2) \
__user_swpX_asm(data, addr, res, temp, "") __user_swpX_asm(data, addr, res, temp, temp2, "")
#define __user_swpb_asm(data, addr, res, temp) \ #define __user_swpb_asm(data, addr, res, temp, temp2) \
__user_swpX_asm(data, addr, res, temp, "b") __user_swpX_asm(data, addr, res, temp, temp2, "b")
/* /*
* Bit 22 of the instruction encoding distinguishes between * Bit 22 of the instruction encoding distinguishes between
...@@ -328,12 +336,12 @@ static int emulate_swpX(unsigned int address, unsigned int *data, ...@@ -328,12 +336,12 @@ static int emulate_swpX(unsigned int address, unsigned int *data,
} }
while (1) { while (1) {
unsigned long temp; unsigned long temp, temp2;
if (type == TYPE_SWPB) if (type == TYPE_SWPB)
__user_swpb_asm(*data, address, res, temp); __user_swpb_asm(*data, address, res, temp, temp2);
else else
__user_swp_asm(*data, address, res, temp); __user_swp_asm(*data, address, res, temp, temp2);
if (likely(res != -EAGAIN) || signal_pending(current)) if (likely(res != -EAGAIN) || signal_pending(current))
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment