aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/mm
diff options
context:
space:
mode:
authorVictor Kamensky <kamensky@cisco.com>2017-04-04 01:51:01 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-12 06:41:11 -0400
commit3715dbf77f3bcbd82f447b50e3310f5139028cde (patch)
tree4af41fbf8985cd10cfb54766c81bf8eeeeb041b2 /arch/arm64/mm
parent71b44ef83d2a74e1be9053f80455f62086fae822 (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.c42
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
44static const char *fault_name(unsigned int esr); 44struct 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
52static const struct fault_info fault_info[];
53
54static 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
47static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) 60static 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
484static const struct fault_info { 501static 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
556static 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 */
565asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, 571asmlinkage 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))