]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.18.51/arm64-avoid-returning-from-bad_mode.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.18.51 / arm64-avoid-returning-from-bad_mode.patch
1 From 7d9e8f71b989230bc613d121ca38507d34ada849 Mon Sep 17 00:00:00 2001
2 From: Mark Rutland <mark.rutland@arm.com>
3 Date: Wed, 18 Jan 2017 17:23:41 +0000
4 Subject: arm64: avoid returning from bad_mode
5
6 From: Mark Rutland <mark.rutland@arm.com>
7
8 commit 7d9e8f71b989230bc613d121ca38507d34ada849 upstream.
9
10 Generally, taking an unexpected exception should be a fatal event, and
11 bad_mode is intended to cater for this. However, it should be possible
12 to contain unexpected synchronous exceptions from EL0 without bringing
13 the kernel down, by sending a SIGILL to the task.
14
15 We tried to apply this approach in commit 9955ac47f4ba1c95 ("arm64:
16 don't kill the kernel on a bad esr from el0"), by sending a signal for
17 any bad_mode call resulting from an EL0 exception.
18
19 However, this also applies to other unexpected exceptions, such as
20 SError and FIQ. The entry paths for these exceptions branch to bad_mode
21 without configuring the link register, and have no kernel_exit. Thus, if
22 we take one of these exceptions from EL0, bad_mode will eventually
23 return to the original user link register value.
24
25 This patch fixes this by introducing a new bad_el0_sync handler to cater
26 for the recoverable case, and restoring bad_mode to its original state,
27 whereby it calls panic() and never returns. The recoverable case
28 branches to bad_el0_sync with a bl, and returns to userspace via the
29 usual ret_to_user mechanism.
30
31 Signed-off-by: Mark Rutland <mark.rutland@arm.com>
32 Fixes: 9955ac47f4ba1c95 ("arm64: don't kill the kernel on a bad esr from el0")
33 Reported-by: Mark Salter <msalter@redhat.com>
34 Cc: Will Deacon <will.deacon@arm.com>
35 Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
36 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
37
38
39 ---
40 arch/arm64/kernel/entry.S | 4 ++--
41 arch/arm64/kernel/traps.c | 28 ++++++++++++++++++++++++----
42 2 files changed, 26 insertions(+), 6 deletions(-)
43
44 --- a/arch/arm64/kernel/entry.S
45 +++ b/arch/arm64/kernel/entry.S
46 @@ -551,8 +551,8 @@ el0_inv:
47 mov x0, sp
48 mov x1, #BAD_SYNC
49 mrs x2, esr_el1
50 - adr lr, ret_to_user
51 - b bad_mode
52 + bl bad_el0_sync
53 + b ret_to_user
54 ENDPROC(el0_sync)
55
56 .align 6
57 --- a/arch/arm64/kernel/traps.c
58 +++ b/arch/arm64/kernel/traps.c
59 @@ -308,16 +308,33 @@ asmlinkage long do_ni_syscall(struct pt_
60 }
61
62 /*
63 - * bad_mode handles the impossible case in the exception vector.
64 + * bad_mode handles the impossible case in the exception vector. This is always
65 + * fatal.
66 */
67 asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
68 {
69 - siginfo_t info;
70 - void __user *pc = (void __user *)instruction_pointer(regs);
71 console_verbose();
72
73 pr_crit("Bad mode in %s handler detected, code 0x%08x\n",
74 handler[reason], esr);
75 +
76 + die("Oops - bad mode", regs, 0);
77 + local_irq_disable();
78 + panic("bad mode");
79 +}
80 +
81 +/*
82 + * bad_el0_sync handles unexpected, but potentially recoverable synchronous
83 + * exceptions taken from EL0. Unlike bad_mode, this returns.
84 + */
85 +asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
86 +{
87 + siginfo_t info;
88 + void __user *pc = (void __user *)instruction_pointer(regs);
89 + console_verbose();
90 +
91 + pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x\n",
92 + smp_processor_id(), esr);
93 __show_regs(regs);
94
95 info.si_signo = SIGILL;
96 @@ -325,7 +342,10 @@ asmlinkage void bad_mode(struct pt_regs
97 info.si_code = ILL_ILLOPC;
98 info.si_addr = pc;
99
100 - arm64_notify_die("Oops - bad mode", regs, &info, 0);
101 + current->thread.fault_address = 0;
102 + current->thread.fault_code = 0;
103 +
104 + force_sig_info(info.si_signo, &info, current);
105 }
106
107 void __pte_error(const char *file, int line, unsigned long val)