Commit 98c970da authored by Vincenzo Frascino's avatar Vincenzo Frascino Committed by Linus Torvalds
Browse files

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: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
Co-developed-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Tested-by: default avatarVincenzo 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e5b8d921
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1649,6 +1649,8 @@ config ARM64_MTE
	default y
	depends on ARM64_AS_HAS_MTE && ARM64_TAGGED_ADDR_ABI
	depends on AS_HAS_ARMV8_5
	# Required for tag checking in the uaccess routines
	depends on ARM64_PAN
	select ARCH_USES_HIGH_VMA_FLAGS
	help
	  Memory Tagging (part of the ARMv8.5 Extensions) provides
+23 −0
Original line number Diff line number Diff line
@@ -159,8 +159,28 @@ static inline void __uaccess_enable_hw_pan(void)
			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)
{
	asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(0),
				 ARM64_MTE, CONFIG_KASAN_HW_TAGS));

	if (uaccess_ttbr0_disable())
		return;

@@ -169,6 +189,9 @@ static inline void uaccess_disable_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())
		return;

+45 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/kprobes.h>
#include <asm/mte.h>
#include <asm/processor.h>
#include <asm/sysreg.h>
#include <asm/system_misc.h>
@@ -296,6 +297,44 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
	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,
			      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))
		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 (esr & ESR_ELx_WNR)
			msg = "write to read-only memory";