arm64: mte: add in-kernel tag fault handler
Add the implementation of the in-kernel fault handler. When a tag fault happens on a kernel address: * MTE is disabled on the current CPU, * the execution continues. When a tag fault happens on a user address: * the kernel executes do_bad_area() and panics. The tag fault handler for kernel addresses is currently empty and will be filled in by a future commit. Link: https://lkml.kernel.org/r/20201203102628.GB2224@gaia Link: https://lkml.kernel.org/r/ad31529b073e22840b7a2246172c2b67747ed7c4.1606161801.git.andreyknvl@google.com Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com> Co-developed-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: Andrey Konovalov <andreyknvl@google.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Vincenzo Frascino <vincenzo.frascino@arm.com> Cc: Alexander Potapenko <glider@google.com> Cc: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Branislav Rankov <Branislav.Rankov@arm.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Evgenii Stepanov <eugenis@google.com> Cc: Kevin Brodsky <kevin.brodsky@arm.com> Cc: Marco Elver <elver@google.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Will Deacon <will.deacon@arm.com> [catalin.marinas@arm.com: ensure CONFIG_ARM64_PAN is enabled with MTE] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
e5b8d92189
commit
98c970da8b
@ -1649,6 +1649,8 @@ config ARM64_MTE
|
|||||||
default y
|
default y
|
||||||
depends on ARM64_AS_HAS_MTE && ARM64_TAGGED_ADDR_ABI
|
depends on ARM64_AS_HAS_MTE && ARM64_TAGGED_ADDR_ABI
|
||||||
depends on AS_HAS_ARMV8_5
|
depends on AS_HAS_ARMV8_5
|
||||||
|
# Required for tag checking in the uaccess routines
|
||||||
|
depends on ARM64_PAN
|
||||||
select ARCH_USES_HIGH_VMA_FLAGS
|
select ARCH_USES_HIGH_VMA_FLAGS
|
||||||
help
|
help
|
||||||
Memory Tagging (part of the ARMv8.5 Extensions) provides
|
Memory Tagging (part of the ARMv8.5 Extensions) provides
|
||||||
|
@ -159,8 +159,28 @@ static inline void __uaccess_enable_hw_pan(void)
|
|||||||
CONFIG_ARM64_PAN));
|
CONFIG_ARM64_PAN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Tag Check Flag (TCF) mode for MTE is per EL, hence TCF0
|
||||||
|
* affects EL0 and TCF affects EL1 irrespective of which TTBR is
|
||||||
|
* used.
|
||||||
|
* The kernel accesses TTBR0 usually with LDTR/STTR instructions
|
||||||
|
* when UAO is available, so these would act as EL0 accesses using
|
||||||
|
* TCF0.
|
||||||
|
* However futex.h code uses exclusives which would be executed as
|
||||||
|
* EL1, this can potentially cause a tag check fault even if the
|
||||||
|
* user disables TCF0.
|
||||||
|
*
|
||||||
|
* To address the problem we set the PSTATE.TCO bit in uaccess_enable()
|
||||||
|
* and reset it in uaccess_disable().
|
||||||
|
*
|
||||||
|
* The Tag check override (TCO) bit disables temporarily the tag checking
|
||||||
|
* preventing the issue.
|
||||||
|
*/
|
||||||
static inline void uaccess_disable_privileged(void)
|
static inline void uaccess_disable_privileged(void)
|
||||||
{
|
{
|
||||||
|
asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(0),
|
||||||
|
ARM64_MTE, CONFIG_KASAN_HW_TAGS));
|
||||||
|
|
||||||
if (uaccess_ttbr0_disable())
|
if (uaccess_ttbr0_disable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -169,6 +189,9 @@ static inline void uaccess_disable_privileged(void)
|
|||||||
|
|
||||||
static inline void uaccess_enable_privileged(void)
|
static inline void uaccess_enable_privileged(void)
|
||||||
{
|
{
|
||||||
|
asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(1),
|
||||||
|
ARM64_MTE, CONFIG_KASAN_HW_TAGS));
|
||||||
|
|
||||||
if (uaccess_ttbr0_enable())
|
if (uaccess_ttbr0_enable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <asm/debug-monitors.h>
|
#include <asm/debug-monitors.h>
|
||||||
#include <asm/esr.h>
|
#include <asm/esr.h>
|
||||||
#include <asm/kprobes.h>
|
#include <asm/kprobes.h>
|
||||||
|
#include <asm/mte.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/sysreg.h>
|
#include <asm/sysreg.h>
|
||||||
#include <asm/system_misc.h>
|
#include <asm/system_misc.h>
|
||||||
@ -296,6 +297,44 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
|
|||||||
do_exit(SIGKILL);
|
do_exit(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void report_tag_fault(unsigned long addr, unsigned int esr,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_tag_recovery(unsigned long addr, unsigned int esr,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
static bool reported;
|
||||||
|
|
||||||
|
if (!READ_ONCE(reported)) {
|
||||||
|
report_tag_fault(addr, esr, regs);
|
||||||
|
WRITE_ONCE(reported, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable MTE Tag Checking on the local CPU for the current EL.
|
||||||
|
* It will be done lazily on the other CPUs when they will hit a
|
||||||
|
* tag fault.
|
||||||
|
*/
|
||||||
|
sysreg_clear_set(sctlr_el1, SCTLR_ELx_TCF_MASK, SCTLR_ELx_TCF_NONE);
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_el1_mte_sync_tag_check_fault(unsigned int esr)
|
||||||
|
{
|
||||||
|
unsigned int ec = ESR_ELx_EC(esr);
|
||||||
|
unsigned int fsc = esr & ESR_ELx_FSC;
|
||||||
|
|
||||||
|
if (ec != ESR_ELx_EC_DABT_CUR)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (fsc == ESR_ELx_FSC_MTE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void __do_kernel_fault(unsigned long addr, unsigned int esr,
|
static void __do_kernel_fault(unsigned long addr, unsigned int esr,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@ -312,6 +351,12 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
|
|||||||
"Ignoring spurious kernel translation fault at virtual address %016lx\n", addr))
|
"Ignoring spurious kernel translation fault at virtual address %016lx\n", addr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (is_el1_mte_sync_tag_check_fault(esr)) {
|
||||||
|
do_tag_recovery(addr, esr, regs);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_el1_permission_fault(addr, esr, regs)) {
|
if (is_el1_permission_fault(addr, esr, regs)) {
|
||||||
if (esr & ESR_ELx_WNR)
|
if (esr & ESR_ELx_WNR)
|
||||||
msg = "write to read-only memory";
|
msg = "write to read-only memory";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user