]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
arm64: Basic Branch Target Identification support
authorDave Martin <Dave.Martin@arm.com>
Mon, 16 Mar 2020 16:50:45 +0000 (16:50 +0000)
committerCatalin Marinas <catalin.marinas@arm.com>
Mon, 16 Mar 2020 17:19:48 +0000 (17:19 +0000)
This patch adds the bare minimum required to expose the ARMv8.5
Branch Target Identification feature to userspace.

By itself, this does _not_ automatically enable BTI for any initial
executable pages mapped by execve().  This will come later, but for
now it should be possible to enable BTI manually on those pages by
using mprotect() from within the target process.

Other arches already using the generic mman.h are already using
0x10 for arch-specific prot flags, so we use that for PROT_BTI
here.

For consistency, signal handler entry points in BTI guarded pages
are required to be annotated as such, just like any other function.
This blocks a relatively minor attack vector, but comforming
userspace will have the annotations anyway, so we may as well
enforce them.

Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
23 files changed:
Documentation/arm64/cpu-feature-registers.rst
Documentation/arm64/elf_hwcaps.rst
arch/arm64/include/asm/cpucaps.h
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/esr.h
arch/arm64/include/asm/exception.h
arch/arm64/include/asm/hwcap.h
arch/arm64/include/asm/mman.h [new file with mode: 0644]
arch/arm64/include/asm/pgtable-hwdef.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/ptrace.h
arch/arm64/include/asm/sysreg.h
arch/arm64/include/uapi/asm/hwcap.h
arch/arm64/include/uapi/asm/mman.h [new file with mode: 0644]
arch/arm64/include/uapi/asm/ptrace.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/cpuinfo.c
arch/arm64/kernel/entry-common.c
arch/arm64/kernel/ptrace.c
arch/arm64/kernel/signal.c
arch/arm64/kernel/syscall.c
arch/arm64/kernel/traps.c
include/linux/mm.h

index 41937a8091aaa2a9c2252fc3f98615a4bbb25436..314fa5bc2655da3b30fdde5c12e602ae2633ccc5 100644 (file)
@@ -176,6 +176,8 @@ infrastructure:
      +------------------------------+---------+---------+
      | SSBS                         | [7-4]   |    y    |
      +------------------------------+---------+---------+
+     | BT                           | [3-0]   |    y    |
+     +------------------------------+---------+---------+
 
 
   4) MIDR_EL1 - Main ID Register
index 7dfb97dfe416058f20ef3863c9185d9a071f46ec..84a9fd2d41b4700d00fe35e2a595156ae03c6668 100644 (file)
@@ -236,6 +236,11 @@ HWCAP2_RNG
 
     Functionality implied by ID_AA64ISAR0_EL1.RNDR == 0b0001.
 
+HWCAP2_BTI
+
+    Functionality implied by ID_AA64PFR0_EL1.BT == 0b0001.
+
+
 4. Unused AT_HWCAP bits
 -----------------------
 
index 865e0253fc1ef3c12e496ef8724aececd7677632..58e776c22aab8b9341e507552705181f3536929f 100644 (file)
@@ -58,7 +58,8 @@
 #define ARM64_WORKAROUND_SPECULATIVE_AT_NVHE   48
 #define ARM64_HAS_E0PD                         49
 #define ARM64_HAS_RNG                          50
+#define ARM64_BTI                              51
 
-#define ARM64_NCAPS                            51
+#define ARM64_NCAPS                            52
 
 #endif /* __ASM_CPUCAPS_H */
index 92ef9539874a663b3bd693c4e834a5cf134a21d6..e3ebcc59e83b5bc1075f4f4b69bef8a072442358 100644 (file)
@@ -613,6 +613,12 @@ static inline bool system_has_prio_mask_debugging(void)
               system_uses_irq_prio_masking();
 }
 
+static inline bool system_supports_bti(void)
+{
+       return IS_ENABLED(CONFIG_ARM64_BTI) &&
+               cpus_have_const_cap(ARM64_BTI);
+}
+
 static inline bool system_capabilities_finalized(void)
 {
        return static_branch_likely(&arm64_const_caps_ready);
index cb29253ae86b00ee09844b567620c7fb2c19e24f..390b8ba67830cd3cb9581820d85a42794f956bbe 100644 (file)
@@ -22,7 +22,7 @@
 #define ESR_ELx_EC_PAC         (0x09)  /* EL2 and above */
 /* Unallocated EC: 0x0A - 0x0B */
 #define ESR_ELx_EC_CP14_64     (0x0C)
-/* Unallocated EC: 0x0d */
+#define ESR_ELx_EC_BTI         (0x0D)
 #define ESR_ELx_EC_ILL         (0x0E)
 /* Unallocated EC: 0x0F - 0x10 */
 #define ESR_ELx_EC_SVC32       (0x11)
index 7a6e81ca23a8e0ed5a11013fc74f3ca82cefd1c5..7577a754d44343b26c700811c54b9155893b1322 100644 (file)
@@ -34,6 +34,7 @@ static inline u32 disr_to_esr(u64 disr)
 asmlinkage void enter_from_user_mode(void);
 void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
 void do_undefinstr(struct pt_regs *regs);
+void do_bti(struct pt_regs *regs);
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
 void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
                        struct pt_regs *regs);
index 0f00265248b5c2d67fab6b9bae98f47dfab8d665..d683bcbf1e7c074fd007a794117a7c66ebe13ea1 100644 (file)
@@ -94,6 +94,7 @@
 #define KERNEL_HWCAP_BF16              __khwcap2_feature(BF16)
 #define KERNEL_HWCAP_DGH               __khwcap2_feature(DGH)
 #define KERNEL_HWCAP_RNG               __khwcap2_feature(RNG)
+#define KERNEL_HWCAP_BTI               __khwcap2_feature(BTI)
 
 /*
  * This yields a mask that user programs can use to figure out what
diff --git a/arch/arm64/include/asm/mman.h b/arch/arm64/include/asm/mman.h
new file mode 100644 (file)
index 0000000..081ec8d
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MMAN_H__
+#define __ASM_MMAN_H__
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <uapi/asm/mman.h>
+
+static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot,
+       unsigned long pkey __always_unused)
+{
+       if (system_supports_bti() && (prot & PROT_BTI))
+               return VM_ARM64_BTI;
+
+       return 0;
+}
+#define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey)
+
+static inline pgprot_t arch_vm_get_page_prot(unsigned long vm_flags)
+{
+       return (vm_flags & VM_ARM64_BTI) ? __pgprot(PTE_GP) : __pgprot(0);
+}
+#define arch_vm_get_page_prot(vm_flags) arch_vm_get_page_prot(vm_flags)
+
+static inline bool arch_validate_prot(unsigned long prot,
+       unsigned long addr __always_unused)
+{
+       unsigned long supported = PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM;
+
+       if (system_supports_bti())
+               supported |= PROT_BTI;
+
+       return (prot & ~supported) == 0;
+}
+#define arch_validate_prot(prot, addr) arch_validate_prot(prot, addr)
+
+#endif /* ! __ASM_MMAN_H__ */
index 6bf5e650da7883ba560f2f43a037d4f69facc3e4..167f1d1d48aa64ef4d2346646ae247c84db2a047 100644 (file)
 #define PTE_SHARED             (_AT(pteval_t, 3) << 8)         /* SH[1:0], inner shareable */
 #define PTE_AF                 (_AT(pteval_t, 1) << 10)        /* Access Flag */
 #define PTE_NG                 (_AT(pteval_t, 1) << 11)        /* nG */
+#define PTE_GP                 (_AT(pteval_t, 1) << 50)        /* BTI guarded */
 #define PTE_DBM                        (_AT(pteval_t, 1) << 51)        /* Dirty Bit Management */
 #define PTE_CONT               (_AT(pteval_t, 1) << 52)        /* Contiguous range */
 #define PTE_PXN                        (_AT(pteval_t, 1) << 53)        /* Privileged XN */
index 538c85e62f86d5fbd2f064a66278b917deab240e..4fbf516d8cb238acf0209adbb2746554eab3d938 100644 (file)
@@ -660,7 +660,7 @@ static inline phys_addr_t pgd_page_paddr(pgd_t pgd)
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
        const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY |
-                             PTE_PROT_NONE | PTE_VALID | PTE_WRITE;
+                             PTE_PROT_NONE | PTE_VALID | PTE_WRITE | PTE_GP;
        /* preserve the hardware dirty information */
        if (pte_hw_dirty(pte))
                pte = pte_mkdirty(pte);
index bf57308fcd635e0451bdc716c42646518ac72743..2172ec7594ba5435e7dfef1ebd45f629ef87e3b9 100644 (file)
@@ -35,6 +35,7 @@
 #define GIC_PRIO_PSR_I_SET             (1 << 4)
 
 /* Additional SPSR bits not exposed in the UABI */
+
 #define PSR_IL_BIT             (1 << 20)
 
 /* AArch32-specific ptrace requests */
index b91570ff9db14c199cf9d6ea4fdbc6cde4f8ca12..db08ceb4cc9a296883ca2916807be52b06b63f03 100644 (file)
 #endif
 
 /* SCTLR_EL1 specific flags. */
+#define SCTLR_EL1_BT1          (BIT(36))
+#define SCTLR_EL1_BT0          (BIT(35))
 #define SCTLR_EL1_UCI          (BIT(26))
 #define SCTLR_EL1_E0E          (BIT(24))
 #define SCTLR_EL1_SPAN         (BIT(23))
 
 /* id_aa64pfr1 */
 #define ID_AA64PFR1_SSBS_SHIFT         4
+#define ID_AA64PFR1_BT_SHIFT           0
 
 #define ID_AA64PFR1_SSBS_PSTATE_NI     0
 #define ID_AA64PFR1_SSBS_PSTATE_ONLY   1
 #define ID_AA64PFR1_SSBS_PSTATE_INSNS  2
+#define ID_AA64PFR1_BT_BTI             0x1
 
 /* id_aa64zfr0 */
 #define ID_AA64ZFR0_F64MM_SHIFT                56
index 7752d93bb50fa486d8e6df27049775e1d61572e8..2d6ba1c2592ed1d825a7a51fd645fd62f5a25647 100644 (file)
@@ -73,5 +73,6 @@
 #define HWCAP2_BF16            (1 << 14)
 #define HWCAP2_DGH             (1 << 15)
 #define HWCAP2_RNG             (1 << 16)
+#define HWCAP2_BTI             (1 << 17)
 
 #endif /* _UAPI__ASM_HWCAP_H */
diff --git a/arch/arm64/include/uapi/asm/mman.h b/arch/arm64/include/uapi/asm/mman.h
new file mode 100644 (file)
index 0000000..6fdd71e
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__ASM_MMAN_H
+#define _UAPI__ASM_MMAN_H
+
+#include <asm-generic/mman.h>
+
+#define PROT_BTI       0x10            /* BTI guarded page */
+
+#endif /* ! _UAPI__ASM_MMAN_H */
index d1bb5b69f1ce49578823fc84c588a8b86ac04c20..42cbe34d95ceeb3d9ff2d4fed0f20d0726586cea 100644 (file)
@@ -46,6 +46,7 @@
 #define PSR_I_BIT      0x00000080
 #define PSR_A_BIT      0x00000100
 #define PSR_D_BIT      0x00000200
+#define PSR_BTYPE_MASK 0x00000c00
 #define PSR_SSBS_BIT   0x00001000
 #define PSR_PAN_BIT    0x00400000
 #define PSR_UAO_BIT    0x00800000
@@ -55,6 +56,8 @@
 #define PSR_Z_BIT      0x40000000
 #define PSR_N_BIT      0x80000000
 
+#define PSR_BTYPE_SHIFT                10
+
 /*
  * Groups of PSR bits
  */
 #define PSR_x          0x0000ff00      /* Extension            */
 #define PSR_c          0x000000ff      /* Control              */
 
+/* Convenience names for the values of PSTATE.BTYPE */
+#define PSR_BTYPE_NONE         (0b00 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_JC           (0b01 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_C            (0b10 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_J            (0b11 << PSR_BTYPE_SHIFT)
+
 /* syscall emulation path in ptrace */
 #define PTRACE_SYSEMU            31
 #define PTRACE_SYSEMU_SINGLESTEP  32
index 0b6715625cf6232ba15afb6eca44313c9ad33f41..e6d31776e49bddcaa49663ba9e1f50ffb675f8d8 100644 (file)
@@ -179,6 +179,8 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
 
 static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = {
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_SSBS_SHIFT, 4, ID_AA64PFR1_SSBS_PSTATE_NI),
+       ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_BTI),
+                                   FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_BT_SHIFT, 4, 0),
        ARM64_FTR_END,
 };
 
@@ -1347,6 +1349,21 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
 }
 #endif
 
+#ifdef CONFIG_ARM64_BTI
+static void bti_enable(const struct arm64_cpu_capabilities *__unused)
+{
+       /*
+        * Use of X16/X17 for tail-calls and trampolines that jump to
+        * function entry points using BR is a requirement for
+        * marking binaries with GNU_PROPERTY_AARCH64_FEATURE_1_BTI.
+        * So, be strict and forbid other BRs using other registers to
+        * jump onto a PACIxSP instruction:
+        */
+       sysreg_clear_set(sctlr_el1, 0, SCTLR_EL1_BT0 | SCTLR_EL1_BT1);
+       isb();
+}
+#endif /* CONFIG_ARM64_BTI */
+
 static const struct arm64_cpu_capabilities arm64_features[] = {
        {
                .desc = "GIC system register CPU interface",
@@ -1671,6 +1688,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .sign = FTR_UNSIGNED,
                .min_field_value = 1,
        },
+#endif
+#ifdef CONFIG_ARM64_BTI
+       {
+               .desc = "Branch Target Identification",
+               .capability = ARM64_BTI,
+               .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+               .matches = has_cpuid_feature,
+               .cpu_enable = bti_enable,
+               .sys_reg = SYS_ID_AA64PFR1_EL1,
+               .field_pos = ID_AA64PFR1_BT_SHIFT,
+               .min_field_value = ID_AA64PFR1_BT_BTI,
+               .sign = FTR_UNSIGNED,
+       },
 #endif
        {},
 };
@@ -1781,6 +1811,9 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
        HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_F64MM_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_F64MM, CAP_HWCAP, KERNEL_HWCAP_SVEF64MM),
 #endif
        HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_SSBS_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_SSBS_PSTATE_INSNS, CAP_HWCAP, KERNEL_HWCAP_SSBS),
+#ifdef CONFIG_ARM64_BTI
+       HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_BT_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_BT_BTI, CAP_HWCAP, KERNEL_HWCAP_BTI),
+#endif
 #ifdef CONFIG_ARM64_PTR_AUTH
        HWCAP_MULTI_CAP(ptr_auth_hwcap_addr_matches, CAP_HWCAP, KERNEL_HWCAP_PACA),
        HWCAP_MULTI_CAP(ptr_auth_hwcap_gen_matches, CAP_HWCAP, KERNEL_HWCAP_PACG),
index 86136075ae41042ec32bb0b755ef38a7ce06d051..5e47e93b5dc152023fc65134c55027706742101c 100644 (file)
@@ -92,6 +92,7 @@ static const char *const hwcap_str[] = {
        "bf16",
        "dgh",
        "rng",
+       "bti",
        NULL
 };
 
index fde59981445ca9a0845102c905a8520773f618bf..55ec0627f5a7363857012cf1b1fd17f7f7cbb101 100644 (file)
@@ -188,6 +188,14 @@ static void notrace el0_undef(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(el0_undef);
 
+static void notrace el0_bti(struct pt_regs *regs)
+{
+       user_exit_irqoff();
+       local_daif_restore(DAIF_PROCCTX);
+       do_bti(regs);
+}
+NOKPROBE_SYMBOL(el0_bti);
+
 static void notrace el0_inv(struct pt_regs *regs, unsigned long esr)
 {
        user_exit_irqoff();
@@ -255,6 +263,9 @@ asmlinkage void notrace el0_sync_handler(struct pt_regs *regs)
        case ESR_ELx_EC_UNKNOWN:
                el0_undef(regs);
                break;
+       case ESR_ELx_EC_BTI:
+               el0_bti(regs);
+               break;
        case ESR_ELx_EC_BREAKPT_LOW:
        case ESR_ELx_EC_SOFTSTP_LOW:
        case ESR_ELx_EC_WATCHPT_LOW:
index cd6e5fa48b9cd3a156ecdf18267fadeccdd08e37..fd8ac7cf68e70542ac193a30320410b4667a92a0 100644 (file)
@@ -1874,7 +1874,7 @@ void syscall_trace_exit(struct pt_regs *regs)
  */
 #define SPSR_EL1_AARCH64_RES0_BITS \
        (GENMASK_ULL(63, 32) | GENMASK_ULL(27, 25) | GENMASK_ULL(23, 22) | \
-        GENMASK_ULL(20, 13) | GENMASK_ULL(11, 10) | GENMASK_ULL(5, 5))
+        GENMASK_ULL(20, 13) | GENMASK_ULL(5, 5))
 #define SPSR_EL1_AARCH32_RES0_BITS \
        (GENMASK_ULL(63, 32) | GENMASK_ULL(22, 22) | GENMASK_ULL(20, 20))
 
index 339882db5a9159bfd888d933dc5fff235408e475..801d56cdf70176530b6ce95e444714c362a3500b 100644 (file)
@@ -732,6 +732,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
        regs->regs[29] = (unsigned long)&user->next_frame->fp;
        regs->pc = (unsigned long)ka->sa.sa_handler;
 
+       /*
+        * Signal delivery is a (wacky) indirect function call in
+        * userspace, so simulate the same setting of BTYPE as a BLR
+        * <register containing the signal handler entry point>.
+        * Signal delivery to a location in a PROT_BTI guarded page
+        * that is not a function entry point will now trigger a
+        * SIGILL in userspace.
+        *
+        * If the signal handler entry point is not in a PROT_BTI
+        * guarded page, this is harmless.
+        */
+       if (system_supports_bti()) {
+               regs->pstate &= ~PSR_BTYPE_MASK;
+               regs->pstate |= PSR_BTYPE_C;
+       }
+
        if (ka->sa.sa_flags & SA_RESTORER)
                sigtramp = ka->sa.sa_restorer;
        else
index a12c0c88d3457357de64c32863690726639112f2..5f5b868292f52233f7d2b7f21f1f3d8d1ac5bdb6 100644 (file)
@@ -98,6 +98,24 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
        regs->orig_x0 = regs->regs[0];
        regs->syscallno = scno;
 
+       /*
+        * BTI note:
+        * The architecture does not guarantee that SPSR.BTYPE is zero
+        * on taking an SVC, so we could return to userspace with a
+        * non-zero BTYPE after the syscall.
+        *
+        * This shouldn't matter except when userspace is explicitly
+        * doing something stupid, such as setting PROT_BTI on a page
+        * that lacks conforming BTI/PACIxSP instructions, falling
+        * through from one executable page to another with differing
+        * PROT_BTI, or messing with BTYPE via ptrace: in such cases,
+        * userspace should not be surprised if a SIGILL occurs on
+        * syscall return.
+        *
+        * So, don't touch regs->pstate & PSR_BTYPE_MASK here.
+        * (Similarly for HVC and SMC elsewhere.)
+        */
+
        cortex_a76_erratum_1463225_svc_handler();
        local_daif_restore(DAIF_PROCCTX);
        user_exit();
index cf402be5c573ff882797422ee02bc913d7c17f24..b8c714dda85107d297824b9ba977f9f23b08a98b 100644 (file)
@@ -411,6 +411,13 @@ void do_undefinstr(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(do_undefinstr);
 
+void do_bti(struct pt_regs *regs)
+{
+       BUG_ON(!user_mode(regs));
+       force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc);
+}
+NOKPROBE_SYMBOL(do_bti);
+
 #define __user_cache_maint(insn, address, res)                 \
        if (address >= user_addr_max()) {                       \
                res = -EFAULT;                                  \
@@ -753,6 +760,7 @@ static const char *esr_class_str[] = {
        [ESR_ELx_EC_CP10_ID]            = "CP10 MRC/VMRS",
        [ESR_ELx_EC_PAC]                = "PAC",
        [ESR_ELx_EC_CP14_64]            = "CP14 MCRR/MRRC",
+       [ESR_ELx_EC_BTI]                = "BTI",
        [ESR_ELx_EC_ILL]                = "PSTATE.IL",
        [ESR_ELx_EC_SVC32]              = "SVC (AArch32)",
        [ESR_ELx_EC_HVC32]              = "HVC (AArch32)",
index 52269e56c514d24d318d982b805c5c9e9d7d3082..9e5fce1b2099de4728b301fb03f67e759ae70ae4 100644 (file)
@@ -324,6 +324,9 @@ extern unsigned int kobjsize(const void *objp);
 #elif defined(CONFIG_SPARC64)
 # define VM_SPARC_ADI  VM_ARCH_1       /* Uses ADI tag for access control */
 # define VM_ARCH_CLEAR VM_SPARC_ADI
+#elif defined(CONFIG_ARM64)
+# define VM_ARM64_BTI  VM_ARCH_1       /* BTI guarded page, a.k.a. GP bit */
+# define VM_ARCH_CLEAR VM_ARM64_BTI
 #elif !defined(CONFIG_MMU)
 # define VM_MAPPED_COPY        VM_ARCH_1       /* T if mapped copy of data (nommu mmap) */
 #endif