summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2013-06-05 11:25:13 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-10-05 14:54:15 -0700
commitab3e7055e9b5a99767cbe3a96db798cc68d850cc (patch)
treeb0d3051e22d1d4697e67ef19caa7ee30ec0b9457 /arch/arm
parent961a14671f6f79285672b76740eb87eb44ff5058 (diff)
ARM: 7748/1: oabi: handle faults when loading swi instruction from userspace
commit 1aa2b3b7a6c4f3dbd3671171113a20e6a6190e3b upstream. Running an OABI_COMPAT kernel on an SMP platform can lead to fun and games with page aging. If one CPU issues a swi instruction immediately before another CPU decides to mkold the page containing the swi instruction, then we will fault attempting to load the instruction during the vector_swi handler in order to retrieve its immediate field. Since this fault is not currently dealt with by our exception tables, this results in a panic: Unable to handle kernel paging request at virtual address 4020841c pgd = c490c000 [4020841c] *pgd=84451831, *pte=bf05859d, *ppte=00000000 Internal error: Oops: 17 [#1] PREEMPT SMP ARM Modules linked in: hid_sony(O) CPU: 1 Tainted: G W O (3.4.0-perf-gf496dca-01162-gcbcc62b #1) PC is at vector_swi+0x28/0x88 LR is at 0x40208420 This patch wraps all of the swi instruction loads with the USER macro and provides a shared exception table entry which simply rewinds the saved user PC and returns from the system call (without setting tbl, so there's no worries with tracing or syscall restarting). Returning to userspace will re-enter the page fault handler, from where we will probably send SIGSEGV to the current task. Reported-by: Wang, Yalin <yalin.wang@sonymobile.com> Reviewed-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Cc: Sheng Yong <shengyong1@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/kernel/entry-common.S42
1 files changed, 29 insertions, 13 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index bc5bc0a97131..4bc816a74a2e 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -362,6 +362,16 @@ ENTRY(vector_swi)
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
zero_fp
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr ip, __cr_alignment
+ ldr ip, [ip]
+ mcr p15, 0, ip, c1, c0 @ update control register
+#endif
+
+ enable_irq
+ ct_user_exit
+ get_thread_info tsk
+
/*
* Get the system call number.
*/
@@ -375,9 +385,9 @@ ENTRY(vector_swi)
#ifdef CONFIG_ARM_THUMB
tst r8, #PSR_T_BIT
movne r10, #0 @ no thumb OABI emulation
- ldreq r10, [lr, #-4] @ get SWI instruction
+ USER( ldreq r10, [lr, #-4] ) @ get SWI instruction
#else
- ldr r10, [lr, #-4] @ get SWI instruction
+ USER( ldr r10, [lr, #-4] ) @ get SWI instruction
#endif
#ifdef CONFIG_CPU_ENDIAN_BE8
rev r10, r10 @ little endian instruction
@@ -392,22 +402,13 @@ ENTRY(vector_swi)
/* Legacy ABI only, possibly thumb mode. */
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
- ldreq scno, [lr, #-4]
+ USER( ldreq scno, [lr, #-4] )
#else
/* Legacy ABI only. */
- ldr scno, [lr, #-4] @ get SWI instruction
+ USER( ldr scno, [lr, #-4] ) @ get SWI instruction
#endif
-#ifdef CONFIG_ALIGNMENT_TRAP
- ldr ip, __cr_alignment
- ldr ip, [ip]
- mcr p15, 0, ip, c1, c0 @ update control register
-#endif
- enable_irq
- ct_user_exit
-
- get_thread_info tsk
adr tbl, sys_call_table @ load syscall table pointer
#if defined(CONFIG_OABI_COMPAT)
@@ -442,6 +443,21 @@ local_restart:
eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back
bcs arm_syscall
b sys_ni_syscall @ not private func
+
+#if defined(CONFIG_OABI_COMPAT) || !defined(CONFIG_AEABI)
+ /*
+ * We failed to handle a fault trying to access the page
+ * containing the swi instruction, but we're not really in a
+ * position to return -EFAULT. Instead, return back to the
+ * instruction and re-enter the user fault handling path trying
+ * to page it in. This will likely result in sending SEGV to the
+ * current task.
+ */
+9001:
+ sub lr, lr, #4
+ str lr, [sp, #S_PC]
+ b ret_fast_syscall
+#endif
ENDPROC(vector_swi)
/*