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

arm64: mte: Handle synchronous and asynchronous tag check faults

The Memory Tagging Extension has two modes of notifying a tag check
fault at EL0, configurable through the SCTLR_EL1.TCF0 field:

1. Synchronous raising of a Data Abort exception with DFSC 17.
2. Asynchronous setting of a cumulative bit in TFSRE0_EL1.

Add the exception handler for the synchronous exception and handling of
the asynchronous TFSRE0_EL1.TF0 bit setting via a new TIF flag in
do_notify_resume().

On a tag check failure in user-space, whether synchronous or
asynchronous, a SIGSEGV will be raised on the faulting thread.

Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Co-developed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>

authored by

Vincenzo Frascino and committed by
Catalin Marinas
637ec831 74f10824

+119 -3
+23
arch/arm64/include/asm/mte.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020 ARM Ltd. 4 + */ 5 + #ifndef __ASM_MTE_H 6 + #define __ASM_MTE_H 7 + 8 + #ifndef __ASSEMBLY__ 9 + 10 + #ifdef CONFIG_ARM64_MTE 11 + 12 + void flush_mte_state(void); 13 + 14 + #else 15 + 16 + static inline void flush_mte_state(void) 17 + { 18 + } 19 + 20 + #endif 21 + 22 + #endif /* __ASSEMBLY__ */ 23 + #endif /* __ASM_MTE_H */
+3 -1
arch/arm64/include/asm/thread_info.h
··· 67 67 #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */ 68 68 #define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */ 69 69 #define TIF_FSCHECK 5 /* Check FS is USER_DS on return */ 70 + #define TIF_MTE_ASYNC_FAULT 6 /* MTE Asynchronous Tag Check Fault */ 70 71 #define TIF_SYSCALL_TRACE 8 /* syscall trace active */ 71 72 #define TIF_SYSCALL_AUDIT 9 /* syscall auditing */ 72 73 #define TIF_SYSCALL_TRACEPOINT 10 /* syscall tracepoint for ftrace */ ··· 97 96 #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) 98 97 #define _TIF_32BIT (1 << TIF_32BIT) 99 98 #define _TIF_SVE (1 << TIF_SVE) 99 + #define _TIF_MTE_ASYNC_FAULT (1 << TIF_MTE_ASYNC_FAULT) 100 100 101 101 #define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ 102 102 _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \ 103 - _TIF_UPROBE | _TIF_FSCHECK) 103 + _TIF_UPROBE | _TIF_FSCHECK | _TIF_MTE_ASYNC_FAULT) 104 104 105 105 #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ 106 106 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
+1
arch/arm64/kernel/Makefile
··· 62 62 obj-$(CONFIG_ARM64_SSBD) += ssbd.o 63 63 obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o 64 64 obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o 65 + obj-$(CONFIG_ARM64_MTE) += mte.o 65 66 66 67 obj-y += vdso/ probes/ 67 68 obj-$(CONFIG_COMPAT_VDSO) += vdso32/
+37
arch/arm64/kernel/entry.S
··· 149 149 #endif 150 150 .endm 151 151 152 + /* Check for MTE asynchronous tag check faults */ 153 + .macro check_mte_async_tcf, flgs, tmp 154 + #ifdef CONFIG_ARM64_MTE 155 + alternative_if_not ARM64_MTE 156 + b 1f 157 + alternative_else_nop_endif 158 + mrs_s \tmp, SYS_TFSRE0_EL1 159 + tbz \tmp, #SYS_TFSR_EL1_TF0_SHIFT, 1f 160 + /* Asynchronous TCF occurred for TTBR0 access, set the TI flag */ 161 + orr \flgs, \flgs, #_TIF_MTE_ASYNC_FAULT 162 + str \flgs, [tsk, #TSK_TI_FLAGS] 163 + msr_s SYS_TFSRE0_EL1, xzr 164 + 1: 165 + #endif 166 + .endm 167 + 168 + /* Clear the MTE asynchronous tag check faults */ 169 + .macro clear_mte_async_tcf 170 + #ifdef CONFIG_ARM64_MTE 171 + alternative_if ARM64_MTE 172 + dsb ish 173 + msr_s SYS_TFSRE0_EL1, xzr 174 + alternative_else_nop_endif 175 + #endif 176 + .endm 177 + 152 178 .macro kernel_entry, el, regsize = 64 153 179 .if \regsize == 32 154 180 mov w0, w0 // zero upper 32 bits of x0 ··· 208 182 ldr x19, [tsk, #TSK_TI_FLAGS] 209 183 disable_step_tsk x19, x20 210 184 185 + /* Check for asynchronous tag check faults in user space */ 186 + check_mte_async_tcf x19, x22 211 187 apply_ssbd 1, x22, x23 212 188 213 189 ptrauth_keys_install_kernel tsk, x20, x22, x23 ··· 260 232 mrs_s x20, SYS_ICC_PMR_EL1 261 233 str x20, [sp, #S_PMR_SAVE] 262 234 alternative_else_nop_endif 235 + 236 + /* Re-enable tag checking (TCO set on exception entry) */ 237 + #ifdef CONFIG_ARM64_MTE 238 + alternative_if ARM64_MTE 239 + SET_PSTATE_TCO(0) 240 + alternative_else_nop_endif 241 + #endif 263 242 264 243 /* 265 244 * Registers that may be useful after this macro is invoked: ··· 779 744 and x2, x1, #_TIF_WORK_MASK 780 745 cbnz x2, work_pending 781 746 finish_ret_to_user: 747 + /* Ignore asynchronous tag check faults in the uaccess routines */ 748 + clear_mte_async_tcf 782 749 enable_step_tsk x1, x2 783 750 #ifdef CONFIG_GCC_PLUGIN_STACKLEAK 784 751 bl stackleak_erase
+21
arch/arm64/kernel/mte.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 ARM Ltd. 4 + */ 5 + 6 + #include <linux/thread_info.h> 7 + 8 + #include <asm/cpufeature.h> 9 + #include <asm/mte.h> 10 + #include <asm/sysreg.h> 11 + 12 + void flush_mte_state(void) 13 + { 14 + if (!system_supports_mte()) 15 + return; 16 + 17 + /* clear any pending asynchronous tag fault */ 18 + dsb(ish); 19 + write_sysreg_s(0, SYS_TFSRE0_EL1); 20 + clear_thread_flag(TIF_MTE_ASYNC_FAULT); 21 + }
+7 -1
arch/arm64/kernel/process.c
··· 52 52 #include <asm/exec.h> 53 53 #include <asm/fpsimd.h> 54 54 #include <asm/mmu_context.h> 55 + #include <asm/mte.h> 55 56 #include <asm/processor.h> 56 57 #include <asm/pointer_auth.h> 57 58 #include <asm/stacktrace.h> ··· 240 239 const char *btype_str = btypes[(pstate & PSR_BTYPE_MASK) >> 241 240 PSR_BTYPE_SHIFT]; 242 241 243 - printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO BTYPE=%s)\n", 242 + printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO %cTCO BTYPE=%s)\n", 244 243 pstate, 245 244 pstate & PSR_N_BIT ? 'N' : 'n', 246 245 pstate & PSR_Z_BIT ? 'Z' : 'z', ··· 252 251 pstate & PSR_F_BIT ? 'F' : 'f', 253 252 pstate & PSR_PAN_BIT ? '+' : '-', 254 253 pstate & PSR_UAO_BIT ? '+' : '-', 254 + pstate & PSR_TCO_BIT ? '+' : '-', 255 255 btype_str); 256 256 } 257 257 } ··· 338 336 tls_thread_flush(); 339 337 flush_ptrace_hw_breakpoint(current); 340 338 flush_tagged_addr_state(); 339 + flush_mte_state(); 341 340 } 342 341 343 342 void release_thread(struct task_struct *dead_task) ··· 370 367 */ 371 368 dst->thread.sve_state = NULL; 372 369 clear_tsk_thread_flag(dst, TIF_SVE); 370 + 371 + /* clear any pending asynchronous tag fault raised by the parent */ 372 + clear_tsk_thread_flag(dst, TIF_MTE_ASYNC_FAULT); 373 373 374 374 return 0; 375 375 }
+9
arch/arm64/kernel/signal.c
··· 748 748 regs->pstate |= PSR_BTYPE_C; 749 749 } 750 750 751 + /* TCO (Tag Check Override) always cleared for signal handlers */ 752 + regs->pstate &= ~PSR_TCO_BIT; 753 + 751 754 if (ka->sa.sa_flags & SA_RESTORER) 752 755 sigtramp = ka->sa.sa_restorer; 753 756 else ··· 934 931 935 932 if (thread_flags & _TIF_UPROBE) 936 933 uprobe_notify_resume(regs); 934 + 935 + if (thread_flags & _TIF_MTE_ASYNC_FAULT) { 936 + clear_thread_flag(TIF_MTE_ASYNC_FAULT); 937 + send_sig_fault(SIGSEGV, SEGV_MTEAERR, 938 + (void __user *)NULL, current); 939 + } 937 940 938 941 if (thread_flags & _TIF_SIGPENDING) 939 942 do_signal(regs);
+10
arch/arm64/kernel/syscall.c
··· 123 123 local_daif_restore(DAIF_PROCCTX); 124 124 user_exit(); 125 125 126 + if (system_supports_mte() && (flags & _TIF_MTE_ASYNC_FAULT)) { 127 + /* 128 + * Process the asynchronous tag check fault before the actual 129 + * syscall. do_notify_resume() will send a signal to userspace 130 + * before the syscall is restarted. 131 + */ 132 + regs->regs[0] = -ERESTARTNOINTR; 133 + return; 134 + } 135 + 126 136 if (has_syscall_work(flags)) { 127 137 /* 128 138 * The de-facto standard way to skip a system call using ptrace
+8 -1
arch/arm64/mm/fault.c
··· 641 641 return 0; 642 642 } 643 643 644 + static int do_tag_check_fault(unsigned long addr, unsigned int esr, 645 + struct pt_regs *regs) 646 + { 647 + do_bad_area(addr, esr, regs); 648 + return 0; 649 + } 650 + 644 651 static const struct fault_info fault_info[] = { 645 652 { do_bad, SIGKILL, SI_KERNEL, "ttbr address size fault" }, 646 653 { do_bad, SIGKILL, SI_KERNEL, "level 1 address size fault" }, ··· 666 659 { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, 667 660 { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, 668 661 { do_sea, SIGBUS, BUS_OBJERR, "synchronous external abort" }, 669 - { do_bad, SIGKILL, SI_KERNEL, "unknown 17" }, 662 + { do_tag_check_fault, SIGSEGV, SEGV_MTESERR, "synchronous tag check fault" }, 670 663 { do_bad, SIGKILL, SI_KERNEL, "unknown 18" }, 671 664 { do_bad, SIGKILL, SI_KERNEL, "unknown 19" }, 672 665 { do_sea, SIGKILL, SI_KERNEL, "level 0 (translation table walk)" },