mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
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>
This commit is contained in:
parent
74f1082487
commit
637ec831ea
9 changed files with 119 additions and 3 deletions
23
arch/arm64/include/asm/mte.h
Normal file
23
arch/arm64/include/asm/mte.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 ARM Ltd.
|
||||
*/
|
||||
#ifndef __ASM_MTE_H
|
||||
#define __ASM_MTE_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_ARM64_MTE
|
||||
|
||||
void flush_mte_state(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline void flush_mte_state(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASM_MTE_H */
|
|
@ -67,6 +67,7 @@ void arch_release_task_struct(struct task_struct *tsk);
|
|||
#define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */
|
||||
#define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */
|
||||
#define TIF_FSCHECK 5 /* Check FS is USER_DS on return */
|
||||
#define TIF_MTE_ASYNC_FAULT 6 /* MTE Asynchronous Tag Check Fault */
|
||||
#define TIF_SYSCALL_TRACE 8 /* syscall trace active */
|
||||
#define TIF_SYSCALL_AUDIT 9 /* syscall auditing */
|
||||
#define TIF_SYSCALL_TRACEPOINT 10 /* syscall tracepoint for ftrace */
|
||||
|
@ -96,10 +97,11 @@ void arch_release_task_struct(struct task_struct *tsk);
|
|||
#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
|
||||
#define _TIF_32BIT (1 << TIF_32BIT)
|
||||
#define _TIF_SVE (1 << TIF_SVE)
|
||||
#define _TIF_MTE_ASYNC_FAULT (1 << TIF_MTE_ASYNC_FAULT)
|
||||
|
||||
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
|
||||
_TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
|
||||
_TIF_UPROBE | _TIF_FSCHECK)
|
||||
_TIF_UPROBE | _TIF_FSCHECK | _TIF_MTE_ASYNC_FAULT)
|
||||
|
||||
#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
|
||||
_TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
|
||||
|
|
|
@ -62,6 +62,7 @@ obj-$(CONFIG_ARM_SDE_INTERFACE) += sdei.o
|
|||
obj-$(CONFIG_ARM64_SSBD) += ssbd.o
|
||||
obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o
|
||||
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
|
||||
obj-$(CONFIG_ARM64_MTE) += mte.o
|
||||
|
||||
obj-y += vdso/ probes/
|
||||
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
|
||||
|
|
|
@ -149,6 +149,32 @@ alternative_cb_end
|
|||
#endif
|
||||
.endm
|
||||
|
||||
/* Check for MTE asynchronous tag check faults */
|
||||
.macro check_mte_async_tcf, flgs, tmp
|
||||
#ifdef CONFIG_ARM64_MTE
|
||||
alternative_if_not ARM64_MTE
|
||||
b 1f
|
||||
alternative_else_nop_endif
|
||||
mrs_s \tmp, SYS_TFSRE0_EL1
|
||||
tbz \tmp, #SYS_TFSR_EL1_TF0_SHIFT, 1f
|
||||
/* Asynchronous TCF occurred for TTBR0 access, set the TI flag */
|
||||
orr \flgs, \flgs, #_TIF_MTE_ASYNC_FAULT
|
||||
str \flgs, [tsk, #TSK_TI_FLAGS]
|
||||
msr_s SYS_TFSRE0_EL1, xzr
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Clear the MTE asynchronous tag check faults */
|
||||
.macro clear_mte_async_tcf
|
||||
#ifdef CONFIG_ARM64_MTE
|
||||
alternative_if ARM64_MTE
|
||||
dsb ish
|
||||
msr_s SYS_TFSRE0_EL1, xzr
|
||||
alternative_else_nop_endif
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro kernel_entry, el, regsize = 64
|
||||
.if \regsize == 32
|
||||
mov w0, w0 // zero upper 32 bits of x0
|
||||
|
@ -182,6 +208,8 @@ alternative_cb_end
|
|||
ldr x19, [tsk, #TSK_TI_FLAGS]
|
||||
disable_step_tsk x19, x20
|
||||
|
||||
/* Check for asynchronous tag check faults in user space */
|
||||
check_mte_async_tcf x19, x22
|
||||
apply_ssbd 1, x22, x23
|
||||
|
||||
ptrauth_keys_install_kernel tsk, x20, x22, x23
|
||||
|
@ -233,6 +261,13 @@ alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
|||
str x20, [sp, #S_PMR_SAVE]
|
||||
alternative_else_nop_endif
|
||||
|
||||
/* Re-enable tag checking (TCO set on exception entry) */
|
||||
#ifdef CONFIG_ARM64_MTE
|
||||
alternative_if ARM64_MTE
|
||||
SET_PSTATE_TCO(0)
|
||||
alternative_else_nop_endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Registers that may be useful after this macro is invoked:
|
||||
*
|
||||
|
@ -744,6 +779,8 @@ SYM_CODE_START_LOCAL(ret_to_user)
|
|||
and x2, x1, #_TIF_WORK_MASK
|
||||
cbnz x2, work_pending
|
||||
finish_ret_to_user:
|
||||
/* Ignore asynchronous tag check faults in the uaccess routines */
|
||||
clear_mte_async_tcf
|
||||
enable_step_tsk x1, x2
|
||||
#ifdef CONFIG_GCC_PLUGIN_STACKLEAK
|
||||
bl stackleak_erase
|
||||
|
|
21
arch/arm64/kernel/mte.c
Normal file
21
arch/arm64/kernel/mte.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/mte.h>
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
void flush_mte_state(void)
|
||||
{
|
||||
if (!system_supports_mte())
|
||||
return;
|
||||
|
||||
/* clear any pending asynchronous tag fault */
|
||||
dsb(ish);
|
||||
write_sysreg_s(0, SYS_TFSRE0_EL1);
|
||||
clear_thread_flag(TIF_MTE_ASYNC_FAULT);
|
||||
}
|
|
@ -52,6 +52,7 @@
|
|||
#include <asm/exec.h>
|
||||
#include <asm/fpsimd.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/mte.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pointer_auth.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
@ -239,7 +240,7 @@ static void print_pstate(struct pt_regs *regs)
|
|||
const char *btype_str = btypes[(pstate & PSR_BTYPE_MASK) >>
|
||||
PSR_BTYPE_SHIFT];
|
||||
|
||||
printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO BTYPE=%s)\n",
|
||||
printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO %cTCO BTYPE=%s)\n",
|
||||
pstate,
|
||||
pstate & PSR_N_BIT ? 'N' : 'n',
|
||||
pstate & PSR_Z_BIT ? 'Z' : 'z',
|
||||
|
@ -251,6 +252,7 @@ static void print_pstate(struct pt_regs *regs)
|
|||
pstate & PSR_F_BIT ? 'F' : 'f',
|
||||
pstate & PSR_PAN_BIT ? '+' : '-',
|
||||
pstate & PSR_UAO_BIT ? '+' : '-',
|
||||
pstate & PSR_TCO_BIT ? '+' : '-',
|
||||
btype_str);
|
||||
}
|
||||
}
|
||||
|
@ -336,6 +338,7 @@ void flush_thread(void)
|
|||
tls_thread_flush();
|
||||
flush_ptrace_hw_breakpoint(current);
|
||||
flush_tagged_addr_state();
|
||||
flush_mte_state();
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
|
@ -368,6 +371,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
|
|||
dst->thread.sve_state = NULL;
|
||||
clear_tsk_thread_flag(dst, TIF_SVE);
|
||||
|
||||
/* clear any pending asynchronous tag fault raised by the parent */
|
||||
clear_tsk_thread_flag(dst, TIF_MTE_ASYNC_FAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -748,6 +748,9 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
|
|||
regs->pstate |= PSR_BTYPE_C;
|
||||
}
|
||||
|
||||
/* TCO (Tag Check Override) always cleared for signal handlers */
|
||||
regs->pstate &= ~PSR_TCO_BIT;
|
||||
|
||||
if (ka->sa.sa_flags & SA_RESTORER)
|
||||
sigtramp = ka->sa.sa_restorer;
|
||||
else
|
||||
|
@ -932,6 +935,12 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
|
|||
if (thread_flags & _TIF_UPROBE)
|
||||
uprobe_notify_resume(regs);
|
||||
|
||||
if (thread_flags & _TIF_MTE_ASYNC_FAULT) {
|
||||
clear_thread_flag(TIF_MTE_ASYNC_FAULT);
|
||||
send_sig_fault(SIGSEGV, SEGV_MTEAERR,
|
||||
(void __user *)NULL, current);
|
||||
}
|
||||
|
||||
if (thread_flags & _TIF_SIGPENDING)
|
||||
do_signal(regs);
|
||||
|
||||
|
|
|
@ -123,6 +123,16 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
|
|||
local_daif_restore(DAIF_PROCCTX);
|
||||
user_exit();
|
||||
|
||||
if (system_supports_mte() && (flags & _TIF_MTE_ASYNC_FAULT)) {
|
||||
/*
|
||||
* Process the asynchronous tag check fault before the actual
|
||||
* syscall. do_notify_resume() will send a signal to userspace
|
||||
* before the syscall is restarted.
|
||||
*/
|
||||
regs->regs[0] = -ERESTARTNOINTR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (has_syscall_work(flags)) {
|
||||
/*
|
||||
* The de-facto standard way to skip a system call using ptrace
|
||||
|
|
|
@ -641,6 +641,13 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int do_tag_check_fault(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
do_bad_area(addr, esr, regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fault_info fault_info[] = {
|
||||
{ do_bad, SIGKILL, SI_KERNEL, "ttbr address size fault" },
|
||||
{ do_bad, SIGKILL, SI_KERNEL, "level 1 address size fault" },
|
||||
|
@ -659,7 +666,7 @@ static const struct fault_info fault_info[] = {
|
|||
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
|
||||
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
|
||||
{ do_sea, SIGBUS, BUS_OBJERR, "synchronous external abort" },
|
||||
{ do_bad, SIGKILL, SI_KERNEL, "unknown 17" },
|
||||
{ do_tag_check_fault, SIGSEGV, SEGV_MTESERR, "synchronous tag check fault" },
|
||||
{ do_bad, SIGKILL, SI_KERNEL, "unknown 18" },
|
||||
{ do_bad, SIGKILL, SI_KERNEL, "unknown 19" },
|
||||
{ do_sea, SIGKILL, SI_KERNEL, "level 0 (translation table walk)" },
|
||||
|
|
Loading…
Reference in a new issue