diff options
author | Victor Kamensky <kamensky@cisco.com> | 2017-04-04 01:51:01 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-12 06:41:11 -0400 |
commit | 3715dbf77f3bcbd82f447b50e3310f5139028cde (patch) | |
tree | 4af41fbf8985cd10cfb54766c81bf8eeeeb041b2 /arch/arm64/mm | |
parent | 71b44ef83d2a74e1be9053f80455f62086fae822 (diff) |
arm64: mm: unaligned access by user-land should be received as SIGBUS
commit 09a6adf53d42ca3088fa3fb41f40b768efc711ed upstream.
After 52d7523 (arm64: mm: allow the kernel to handle alignment faults on
user accesses) commit user-land accesses that produce unaligned exceptions
like in case of aarch32 ldm/stm/ldrd/strd instructions operating on
unaligned memory received by user-land as SIGSEGV. It is wrong, it should
be reported as SIGBUS as it was before 52d7523 commit.
Changed do_bad_area function to take signal and code parameters out of esr
value using fault_info table, so in case of do_alignment_fault fault
user-land will receive SIGBUS. Wrapped access to fault_info table into
esr_to_fault_info function.
Fixes: 52d7523 (arm64: mm: allow the kernel to handle alignment faults on user accesses)
Signed-off-by: Victor Kamensky <kamensky@cisco.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/arm64/mm')
-rw-r--r-- | arch/arm64/mm/fault.c | 42 |
1 files changed, 24 insertions, 18 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 0f8788374815..8b8ac3db4092 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -41,7 +41,20 @@ | |||
41 | #include <asm/pgtable.h> | 41 | #include <asm/pgtable.h> |
42 | #include <asm/tlbflush.h> | 42 | #include <asm/tlbflush.h> |
43 | 43 | ||
44 | static const char *fault_name(unsigned int esr); | 44 | struct fault_info { |
45 | int (*fn)(unsigned long addr, unsigned int esr, | ||
46 | struct pt_regs *regs); | ||
47 | int sig; | ||
48 | int code; | ||
49 | const char *name; | ||
50 | }; | ||
51 | |||
52 | static const struct fault_info fault_info[]; | ||
53 | |||
54 | static inline const struct fault_info *esr_to_fault_info(unsigned int esr) | ||
55 | { | ||
56 | return fault_info + (esr & 63); | ||
57 | } | ||
45 | 58 | ||
46 | #ifdef CONFIG_KPROBES | 59 | #ifdef CONFIG_KPROBES |
47 | static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) | 60 | static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) |
@@ -196,10 +209,12 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | |||
196 | struct pt_regs *regs) | 209 | struct pt_regs *regs) |
197 | { | 210 | { |
198 | struct siginfo si; | 211 | struct siginfo si; |
212 | const struct fault_info *inf; | ||
199 | 213 | ||
200 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { | 214 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { |
215 | inf = esr_to_fault_info(esr); | ||
201 | pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", | 216 | pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", |
202 | tsk->comm, task_pid_nr(tsk), fault_name(esr), sig, | 217 | tsk->comm, task_pid_nr(tsk), inf->name, sig, |
203 | addr, esr); | 218 | addr, esr); |
204 | show_pte(tsk->mm, addr); | 219 | show_pte(tsk->mm, addr); |
205 | show_regs(regs); | 220 | show_regs(regs); |
@@ -218,14 +233,16 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re | |||
218 | { | 233 | { |
219 | struct task_struct *tsk = current; | 234 | struct task_struct *tsk = current; |
220 | struct mm_struct *mm = tsk->active_mm; | 235 | struct mm_struct *mm = tsk->active_mm; |
236 | const struct fault_info *inf; | ||
221 | 237 | ||
222 | /* | 238 | /* |
223 | * If we are in kernel mode at this point, we have no context to | 239 | * If we are in kernel mode at this point, we have no context to |
224 | * handle this fault with. | 240 | * handle this fault with. |
225 | */ | 241 | */ |
226 | if (user_mode(regs)) | 242 | if (user_mode(regs)) { |
227 | __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs); | 243 | inf = esr_to_fault_info(esr); |
228 | else | 244 | __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs); |
245 | } else | ||
229 | __do_kernel_fault(mm, addr, esr, regs); | 246 | __do_kernel_fault(mm, addr, esr, regs); |
230 | } | 247 | } |
231 | 248 | ||
@@ -481,12 +498,7 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) | |||
481 | return 1; | 498 | return 1; |
482 | } | 499 | } |
483 | 500 | ||
484 | static const struct fault_info { | 501 | static const struct fault_info fault_info[] = { |
485 | int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); | ||
486 | int sig; | ||
487 | int code; | ||
488 | const char *name; | ||
489 | } fault_info[] = { | ||
490 | { do_bad, SIGBUS, 0, "ttbr address size fault" }, | 502 | { do_bad, SIGBUS, 0, "ttbr address size fault" }, |
491 | { do_bad, SIGBUS, 0, "level 1 address size fault" }, | 503 | { do_bad, SIGBUS, 0, "level 1 address size fault" }, |
492 | { do_bad, SIGBUS, 0, "level 2 address size fault" }, | 504 | { do_bad, SIGBUS, 0, "level 2 address size fault" }, |
@@ -553,19 +565,13 @@ static const struct fault_info { | |||
553 | { do_bad, SIGBUS, 0, "unknown 63" }, | 565 | { do_bad, SIGBUS, 0, "unknown 63" }, |
554 | }; | 566 | }; |
555 | 567 | ||
556 | static const char *fault_name(unsigned int esr) | ||
557 | { | ||
558 | const struct fault_info *inf = fault_info + (esr & 63); | ||
559 | return inf->name; | ||
560 | } | ||
561 | |||
562 | /* | 568 | /* |
563 | * Dispatch a data abort to the relevant handler. | 569 | * Dispatch a data abort to the relevant handler. |
564 | */ | 570 | */ |
565 | asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, | 571 | asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, |
566 | struct pt_regs *regs) | 572 | struct pt_regs *regs) |
567 | { | 573 | { |
568 | const struct fault_info *inf = fault_info + (esr & 63); | 574 | const struct fault_info *inf = esr_to_fault_info(esr); |
569 | struct siginfo info; | 575 | struct siginfo info; |
570 | 576 | ||
571 | if (!inf->fn(addr, esr, regs)) | 577 | if (!inf->fn(addr, esr, regs)) |