Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: expose FAR_EL1 tag bits in siginfo

The kernel currently clears the tag bits (i.e. bits 56-63) in the fault
address exposed via siginfo.si_addr and sigcontext.fault_address. However,
the tag bits may be needed by tools in order to accurately diagnose
memory errors, such as HWASan [1] or future tools based on the Memory
Tagging Extension (MTE).

Expose these bits via the arch_untagged_si_addr mechanism, so that
they are only exposed to signal handlers with the SA_EXPOSE_TAGBITS
flag set.

[1] http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html

Signed-off-by: Peter Collingbourne <pcc@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://linux-review.googlesource.com/id/Ia8876bad8c798e0a32df7c2ce1256c4771c81446
Link: https://lore.kernel.org/r/0010296597784267472fa13b39f8238d87a72cf8.1605904350.git.pcc@google.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Peter Collingbourne and committed by
Catalin Marinas
dceec3ff 6ac05e83

+121 -72
+19 -6
Documentation/arm64/tagged-pointers.rst
··· 53 53 Preserving tags 54 54 --------------- 55 55 56 - Non-zero tags are not preserved when delivering signals. This means that 57 - signal handlers in applications making use of tags cannot rely on the 58 - tag information for user virtual addresses being maintained for fields 59 - inside siginfo_t. One exception to this rule is for signals raised in 60 - response to watchpoint debug exceptions, where the tag information will 61 - be preserved. 56 + When delivering signals, non-zero tags are not preserved in 57 + siginfo.si_addr unless the flag SA_EXPOSE_TAGBITS was set in 58 + sigaction.sa_flags when the signal handler was installed. This means 59 + that signal handlers in applications making use of tags cannot rely 60 + on the tag information for user virtual addresses being maintained 61 + in these fields unless the flag was set. 62 + 63 + Due to architecture limitations, bits 63:60 of the fault address 64 + are not preserved in response to synchronous tag check faults 65 + (SEGV_MTESERR) even if SA_EXPOSE_TAGBITS was set. Applications should 66 + treat the values of these bits as undefined in order to accommodate 67 + future architecture revisions which may preserve the bits. 68 + 69 + For signals raised in response to watchpoint debug exceptions, the 70 + tag information will be preserved regardless of the SA_EXPOSE_TAGBITS 71 + flag setting. 72 + 73 + Non-zero tags are never preserved in sigcontext.fault_address 74 + regardless of the SA_EXPOSE_TAGBITS flag setting. 62 75 63 76 The architecture prevents the use of a tagged PC, so the upper byte will 64 77 be set to a sign-extension of bit 55 on exception return.
+1 -1
arch/arm64/include/asm/exception.h
··· 32 32 } 33 33 34 34 asmlinkage void enter_from_user_mode(void); 35 - void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs); 35 + void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs); 36 36 void do_undefinstr(struct pt_regs *regs); 37 37 void do_bti(struct pt_regs *regs); 38 38 asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
+25
arch/arm64/include/asm/signal.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __ARM64_ASM_SIGNAL_H 3 + #define __ARM64_ASM_SIGNAL_H 4 + 5 + #include <asm/memory.h> 6 + #include <uapi/asm/signal.h> 7 + #include <uapi/asm/siginfo.h> 8 + 9 + static inline void __user *arch_untagged_si_addr(void __user *addr, 10 + unsigned long sig, 11 + unsigned long si_code) 12 + { 13 + /* 14 + * For historical reasons, all bits of the fault address are exposed as 15 + * address bits for watchpoint exceptions. New architectures should 16 + * handle the tag bits consistently. 17 + */ 18 + if (sig == SIGTRAP && si_code == TRAP_BRKPT) 19 + return addr; 20 + 21 + return untagged_addr(addr); 22 + } 23 + #define arch_untagged_si_addr arch_untagged_si_addr 24 + 25 + #endif
+1 -1
arch/arm64/include/asm/system_misc.h
··· 22 22 23 23 struct siginfo; 24 24 void arm64_notify_die(const char *str, struct pt_regs *regs, 25 - int signo, int sicode, void __user *addr, 25 + int signo, int sicode, unsigned long far, 26 26 int err); 27 27 28 28 void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
+3 -3
arch/arm64/include/asm/traps.h
··· 26 26 void unregister_undef_hook(struct undef_hook *hook); 27 27 void force_signal_inject(int signal, int code, unsigned long address, unsigned int err); 28 28 void arm64_notify_segfault(unsigned long addr); 29 - void arm64_force_sig_fault(int signo, int code, void __user *addr, const char *str); 30 - void arm64_force_sig_mceerr(int code, void __user *addr, short lsb, const char *str); 31 - void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr, const char *str); 29 + void arm64_force_sig_fault(int signo, int code, unsigned long far, const char *str); 30 + void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str); 31 + void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str); 32 32 33 33 /* 34 34 * Move regs->pc to next instruction and do necessary setup before it
+2 -3
arch/arm64/kernel/debug-monitors.c
··· 234 234 if (interrupts_enabled(regs)) 235 235 local_irq_enable(); 236 236 237 - arm64_force_sig_fault(SIGTRAP, si_code, 238 - (void __user *)instruction_pointer(regs), 239 - "User debug trap"); 237 + arm64_force_sig_fault(SIGTRAP, si_code, instruction_pointer(regs), 238 + "User debug trap"); 240 239 } 241 240 242 241 static int single_step_handler(unsigned long unused, unsigned int esr,
-2
arch/arm64/kernel/entry-common.c
··· 22 22 unsigned long far = read_sysreg(far_el1); 23 23 24 24 local_daif_inherit(regs); 25 - far = untagged_addr(far); 26 25 do_mem_abort(far, esr, regs); 27 26 } 28 27 NOKPROBE_SYMBOL(el1_abort); ··· 113 114 114 115 user_exit_irqoff(); 115 116 local_daif_restore(DAIF_PROCCTX); 116 - far = untagged_addr(far); 117 117 do_mem_abort(far, esr, regs); 118 118 } 119 119 NOKPROBE_SYMBOL(el0_da);
+2 -5
arch/arm64/kernel/ptrace.c
··· 192 192 break; 193 193 } 194 194 } 195 - arm64_force_sig_ptrace_errno_trap(si_errno, 196 - (void __user *)bkpt->trigger, 195 + arm64_force_sig_ptrace_errno_trap(si_errno, bkpt->trigger, 197 196 desc); 198 197 } 199 198 #endif 200 - arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT, 201 - (void __user *)(bkpt->trigger), 202 - desc); 199 + arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT, bkpt->trigger, desc); 203 200 } 204 201 205 202 /*
+2 -3
arch/arm64/kernel/sys_compat.c
··· 68 68 */ 69 69 long compat_arm_syscall(struct pt_regs *regs, int scno) 70 70 { 71 - void __user *addr; 71 + unsigned long addr; 72 72 73 73 switch (scno) { 74 74 /* ··· 111 111 break; 112 112 } 113 113 114 - addr = (void __user *)instruction_pointer(regs) - 115 - (compat_thumb_mode(regs) ? 2 : 4); 114 + addr = instruction_pointer(regs) - (compat_thumb_mode(regs) ? 2 : 4); 116 115 117 116 arm64_notify_die("Oops - bad compat syscall(2)", regs, 118 117 SIGILL, ILL_ILLTRP, addr, scno);
+15 -14
arch/arm64/kernel/traps.c
··· 170 170 __show_regs(regs); 171 171 } 172 172 173 - void arm64_force_sig_fault(int signo, int code, void __user *addr, 173 + void arm64_force_sig_fault(int signo, int code, unsigned long far, 174 174 const char *str) 175 175 { 176 176 arm64_show_signal(signo, str); 177 177 if (signo == SIGKILL) 178 178 force_sig(SIGKILL); 179 179 else 180 - force_sig_fault(signo, code, addr); 180 + force_sig_fault(signo, code, (void __user *)far); 181 181 } 182 182 183 - void arm64_force_sig_mceerr(int code, void __user *addr, short lsb, 183 + void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, 184 184 const char *str) 185 185 { 186 186 arm64_show_signal(SIGBUS, str); 187 - force_sig_mceerr(code, addr, lsb); 187 + force_sig_mceerr(code, (void __user *)far, lsb); 188 188 } 189 189 190 - void arm64_force_sig_ptrace_errno_trap(int errno, void __user *addr, 190 + void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, 191 191 const char *str) 192 192 { 193 193 arm64_show_signal(SIGTRAP, str); 194 - force_sig_ptrace_errno_trap(errno, addr); 194 + force_sig_ptrace_errno_trap(errno, (void __user *)far); 195 195 } 196 196 197 197 void arm64_notify_die(const char *str, struct pt_regs *regs, 198 - int signo, int sicode, void __user *addr, 198 + int signo, int sicode, unsigned long far, 199 199 int err) 200 200 { 201 201 if (user_mode(regs)) { ··· 203 203 current->thread.fault_address = 0; 204 204 current->thread.fault_code = err; 205 205 206 - arm64_force_sig_fault(signo, sicode, addr, str); 206 + arm64_force_sig_fault(signo, sicode, far, str); 207 207 } else { 208 208 die(str, regs, err); 209 209 } ··· 374 374 signal = SIGKILL; 375 375 } 376 376 377 - arm64_notify_die(desc, regs, signal, code, (void __user *)address, err); 377 + arm64_notify_die(desc, regs, signal, code, address, err); 378 378 } 379 379 380 380 /* ··· 385 385 int code; 386 386 387 387 mmap_read_lock(current->mm); 388 - if (find_vma(current->mm, addr) == NULL) 388 + if (find_vma(current->mm, untagged_addr(addr)) == NULL) 389 389 code = SEGV_MAPERR; 390 390 else 391 391 code = SEGV_ACCERR; ··· 448 448 449 449 static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs) 450 450 { 451 - unsigned long address; 451 + unsigned long tagged_address, address; 452 452 int rt = ESR_ELx_SYS64_ISS_RT(esr); 453 453 int crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT; 454 454 int ret = 0; 455 455 456 - address = untagged_addr(pt_regs_read_reg(regs, rt)); 456 + tagged_address = pt_regs_read_reg(regs, rt); 457 + address = untagged_addr(tagged_address); 457 458 458 459 switch (crm) { 459 460 case ESR_ELx_SYS64_ISS_CRM_DC_CVAU: /* DC CVAU, gets promoted */ ··· 481 480 } 482 481 483 482 if (ret) 484 - arm64_notify_segfault(address); 483 + arm64_notify_segfault(tagged_address); 485 484 else 486 485 arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); 487 486 } ··· 773 772 */ 774 773 void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr) 775 774 { 776 - void __user *pc = (void __user *)instruction_pointer(regs); 775 + unsigned long pc = instruction_pointer(regs); 777 776 778 777 current->thread.fault_address = 0; 779 778 current->thread.fault_code = esr;
+51 -34
arch/arm64/mm/fault.c
··· 40 40 #include <asm/traps.h> 41 41 42 42 struct fault_info { 43 - int (*fn)(unsigned long addr, unsigned int esr, 43 + int (*fn)(unsigned long far, unsigned int esr, 44 44 struct pt_regs *regs); 45 45 int sig; 46 46 int code; ··· 385 385 current->thread.fault_code = esr; 386 386 } 387 387 388 - static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs) 388 + static void do_bad_area(unsigned long far, unsigned int esr, 389 + struct pt_regs *regs) 389 390 { 391 + unsigned long addr = untagged_addr(far); 392 + 390 393 /* 391 394 * If we are in kernel mode at this point, we have no context to 392 395 * handle this fault with. ··· 398 395 const struct fault_info *inf = esr_to_fault_info(esr); 399 396 400 397 set_thread_esr(addr, esr); 401 - arm64_force_sig_fault(inf->sig, inf->code, (void __user *)addr, 402 - inf->name); 398 + arm64_force_sig_fault(inf->sig, inf->code, far, inf->name); 403 399 } else { 404 400 __do_kernel_fault(addr, esr, regs); 405 401 } ··· 450 448 return (esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM); 451 449 } 452 450 453 - static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, 451 + static int __kprobes do_page_fault(unsigned long far, unsigned int esr, 454 452 struct pt_regs *regs) 455 453 { 456 454 const struct fault_info *inf; ··· 458 456 vm_fault_t fault; 459 457 unsigned long vm_flags = VM_ACCESS_FLAGS; 460 458 unsigned int mm_flags = FAULT_FLAG_DEFAULT; 459 + unsigned long addr = untagged_addr(far); 461 460 462 461 if (kprobe_page_fault(regs, esr)) 463 462 return 0; ··· 570 567 * We had some memory, but were unable to successfully fix up 571 568 * this page fault. 572 569 */ 573 - arm64_force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)addr, 574 - inf->name); 570 + arm64_force_sig_fault(SIGBUS, BUS_ADRERR, far, inf->name); 575 571 } else if (fault & (VM_FAULT_HWPOISON_LARGE | VM_FAULT_HWPOISON)) { 576 572 unsigned int lsb; 577 573 ··· 578 576 if (fault & VM_FAULT_HWPOISON_LARGE) 579 577 lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); 580 578 581 - arm64_force_sig_mceerr(BUS_MCEERR_AR, (void __user *)addr, lsb, 582 - inf->name); 579 + arm64_force_sig_mceerr(BUS_MCEERR_AR, far, lsb, inf->name); 583 580 } else { 584 581 /* 585 582 * Something tried to access memory that isn't in our memory ··· 586 585 */ 587 586 arm64_force_sig_fault(SIGSEGV, 588 587 fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR, 589 - (void __user *)addr, 590 - inf->name); 588 + far, inf->name); 591 589 } 592 590 593 591 return 0; ··· 596 596 return 0; 597 597 } 598 598 599 - static int __kprobes do_translation_fault(unsigned long addr, 599 + static int __kprobes do_translation_fault(unsigned long far, 600 600 unsigned int esr, 601 601 struct pt_regs *regs) 602 602 { 603 - if (is_ttbr0_addr(addr)) 604 - return do_page_fault(addr, esr, regs); 603 + unsigned long addr = untagged_addr(far); 605 604 606 - do_bad_area(addr, esr, regs); 605 + if (is_ttbr0_addr(addr)) 606 + return do_page_fault(far, esr, regs); 607 + 608 + do_bad_area(far, esr, regs); 607 609 return 0; 608 610 } 609 611 610 - static int do_alignment_fault(unsigned long addr, unsigned int esr, 612 + static int do_alignment_fault(unsigned long far, unsigned int esr, 611 613 struct pt_regs *regs) 612 614 { 613 - do_bad_area(addr, esr, regs); 615 + do_bad_area(far, esr, regs); 614 616 return 0; 615 617 } 616 618 617 - static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) 619 + static int do_bad(unsigned long far, unsigned int esr, struct pt_regs *regs) 618 620 { 619 621 return 1; /* "fault" */ 620 622 } 621 623 622 - static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) 624 + static int do_sea(unsigned long far, unsigned int esr, struct pt_regs *regs) 623 625 { 624 626 const struct fault_info *inf; 625 - void __user *siaddr; 627 + unsigned long siaddr; 626 628 627 629 inf = esr_to_fault_info(esr); 628 630 ··· 636 634 return 0; 637 635 } 638 636 639 - if (esr & ESR_ELx_FnV) 640 - siaddr = NULL; 641 - else 642 - siaddr = (void __user *)addr; 637 + if (esr & ESR_ELx_FnV) { 638 + siaddr = 0; 639 + } else { 640 + /* 641 + * The architecture specifies that the tag bits of FAR_EL1 are 642 + * UNKNOWN for synchronous external aborts. Mask them out now 643 + * so that userspace doesn't see them. 644 + */ 645 + siaddr = untagged_addr(far); 646 + } 643 647 arm64_notify_die(inf->name, regs, inf->sig, inf->code, siaddr, esr); 644 648 645 649 return 0; 646 650 } 647 651 648 - static int do_tag_check_fault(unsigned long addr, unsigned int esr, 652 + static int do_tag_check_fault(unsigned long far, unsigned int esr, 649 653 struct pt_regs *regs) 650 654 { 651 - do_bad_area(addr, esr, regs); 655 + /* 656 + * The architecture specifies that bits 63:60 of FAR_EL1 are UNKNOWN for tag 657 + * check faults. Mask them out now so that userspace doesn't see them. 658 + */ 659 + far &= (1UL << 60) - 1; 660 + do_bad_area(far, esr, regs); 652 661 return 0; 653 662 } 654 663 ··· 730 717 { do_bad, SIGKILL, SI_KERNEL, "unknown 63" }, 731 718 }; 732 719 733 - void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs) 720 + void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs) 734 721 { 735 722 const struct fault_info *inf = esr_to_fault_info(esr); 723 + unsigned long addr = untagged_addr(far); 736 724 737 - if (!inf->fn(addr, esr, regs)) 725 + if (!inf->fn(far, esr, regs)) 738 726 return; 739 727 740 728 if (!user_mode(regs)) { ··· 744 730 show_pte(addr); 745 731 } 746 732 747 - arm64_notify_die(inf->name, regs, 748 - inf->sig, inf->code, (void __user *)addr, esr); 733 + /* 734 + * At this point we have an unrecognized fault type whose tag bits may 735 + * have been defined as UNKNOWN. Therefore we only expose the untagged 736 + * address to the signal handler. 737 + */ 738 + arm64_notify_die(inf->name, regs, inf->sig, inf->code, addr, esr); 749 739 } 750 740 NOKPROBE_SYMBOL(do_mem_abort); 751 741 ··· 762 744 763 745 void do_sp_pc_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs) 764 746 { 765 - arm64_notify_die("SP/PC alignment exception", regs, 766 - SIGBUS, BUS_ADRALN, (void __user *)addr, esr); 747 + arm64_notify_die("SP/PC alignment exception", regs, SIGBUS, BUS_ADRALN, 748 + addr, esr); 767 749 } 768 750 NOKPROBE_SYMBOL(do_sp_pc_abort); 769 751 ··· 889 871 arm64_apply_bp_hardening(); 890 872 891 873 if (inf->fn(addr_if_watchpoint, esr, regs)) { 892 - arm64_notify_die(inf->name, regs, 893 - inf->sig, inf->code, (void __user *)pc, esr); 874 + arm64_notify_die(inf->name, regs, inf->sig, inf->code, pc, esr); 894 875 } 895 876 896 877 debug_exception_exit(regs);