diff options
author | Victor Kamensky <kamensky@cisco.com> | 2017-04-04 01:51:01 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-04-04 07:13:36 -0400 |
commit | 09a6adf53d42ca3088fa3fb41f40b768efc711ed (patch) | |
tree | b453a6b17322576ddad32ef6dfdeb26e7b880e16 | |
parent | a71c9a1c779f2499fb2afc0553e543f18aff6edf (diff) |
arm64: mm: unaligned access by user-land should be received as SIGBUS
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.
Cc: <stable@vger.kernel.org>
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>
-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 4bf899fb451b..1b35b8bddbfb 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c | |||
@@ -42,7 +42,20 @@ | |||
42 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
43 | #include <asm/tlbflush.h> | 43 | #include <asm/tlbflush.h> |
44 | 44 | ||
45 | static const char *fault_name(unsigned int esr); | 45 | struct fault_info { |
46 | int (*fn)(unsigned long addr, unsigned int esr, | ||
47 | struct pt_regs *regs); | ||
48 | int sig; | ||
49 | int code; | ||
50 | const char *name; | ||
51 | }; | ||
52 | |||
53 | static const struct fault_info fault_info[]; | ||
54 | |||
55 | static inline const struct fault_info *esr_to_fault_info(unsigned int esr) | ||
56 | { | ||
57 | return fault_info + (esr & 63); | ||
58 | } | ||
46 | 59 | ||
47 | #ifdef CONFIG_KPROBES | 60 | #ifdef CONFIG_KPROBES |
48 | static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) | 61 | static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) |
@@ -197,10 +210,12 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | |||
197 | struct pt_regs *regs) | 210 | struct pt_regs *regs) |
198 | { | 211 | { |
199 | struct siginfo si; | 212 | struct siginfo si; |
213 | const struct fault_info *inf; | ||
200 | 214 | ||
201 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { | 215 | if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { |
216 | inf = esr_to_fault_info(esr); | ||
202 | pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", | 217 | pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", |
203 | tsk->comm, task_pid_nr(tsk), fault_name(esr), sig, | 218 | tsk->comm, task_pid_nr(tsk), inf->name, sig, |
204 | addr, esr); | 219 | addr, esr); |
205 | show_pte(tsk->mm, addr); | 220 | show_pte(tsk->mm, addr); |
206 | show_regs(regs); | 221 | show_regs(regs); |
@@ -219,14 +234,16 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re | |||
219 | { | 234 | { |
220 | struct task_struct *tsk = current; | 235 | struct task_struct *tsk = current; |
221 | struct mm_struct *mm = tsk->active_mm; | 236 | struct mm_struct *mm = tsk->active_mm; |
237 | const struct fault_info *inf; | ||
222 | 238 | ||
223 | /* | 239 | /* |
224 | * If we are in kernel mode at this point, we have no context to | 240 | * If we are in kernel mode at this point, we have no context to |
225 | * handle this fault with. | 241 | * handle this fault with. |
226 | */ | 242 | */ |
227 | if (user_mode(regs)) | 243 | if (user_mode(regs)) { |
228 | __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs); | 244 | inf = esr_to_fault_info(esr); |
229 | else | 245 | __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs); |
246 | } else | ||
230 | __do_kernel_fault(mm, addr, esr, regs); | 247 | __do_kernel_fault(mm, addr, esr, regs); |
231 | } | 248 | } |
232 | 249 | ||
@@ -488,12 +505,7 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) | |||
488 | return 1; | 505 | return 1; |
489 | } | 506 | } |
490 | 507 | ||
491 | static const struct fault_info { | 508 | static const struct fault_info fault_info[] = { |
492 | int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); | ||
493 | int sig; | ||
494 | int code; | ||
495 | const char *name; | ||
496 | } fault_info[] = { | ||
497 | { do_bad, SIGBUS, 0, "ttbr address size fault" }, | 509 | { do_bad, SIGBUS, 0, "ttbr address size fault" }, |
498 | { do_bad, SIGBUS, 0, "level 1 address size fault" }, | 510 | { do_bad, SIGBUS, 0, "level 1 address size fault" }, |
499 | { do_bad, SIGBUS, 0, "level 2 address size fault" }, | 511 | { do_bad, SIGBUS, 0, "level 2 address size fault" }, |
@@ -560,19 +572,13 @@ static const struct fault_info { | |||
560 | { do_bad, SIGBUS, 0, "unknown 63" }, | 572 | { do_bad, SIGBUS, 0, "unknown 63" }, |
561 | }; | 573 | }; |
562 | 574 | ||
563 | static const char *fault_name(unsigned int esr) | ||
564 | { | ||
565 | const struct fault_info *inf = fault_info + (esr & 63); | ||
566 | return inf->name; | ||
567 | } | ||
568 | |||
569 | /* | 575 | /* |
570 | * Dispatch a data abort to the relevant handler. | 576 | * Dispatch a data abort to the relevant handler. |
571 | */ | 577 | */ |
572 | asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, | 578 | asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, |
573 | struct pt_regs *regs) | 579 | struct pt_regs *regs) |
574 | { | 580 | { |
575 | const struct fault_info *inf = fault_info + (esr & 63); | 581 | const struct fault_info *inf = esr_to_fault_info(esr); |
576 | struct siginfo info; | 582 | struct siginfo info; |
577 | 583 | ||
578 | if (!inf->fn(addr, esr, regs)) | 584 | if (!inf->fn(addr, esr, regs)) |