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

arm64: mte: switch GCR_EL1 in kernel entry and exit

When MTE is present, the GCR_EL1 register contains the tags mask that
allows to exclude tags from the random generation via the IRG instruction.

With the introduction of the new Tag-Based KASAN API that provides a
mechanism to reserve tags for special reasons, the MTE implementation has
to make sure that the GCR_EL1 setting for the kernel does not affect the
userspace processes and viceversa.

Save and restore the kernel/user mask in GCR_EL1 in kernel entry and exit.

Link: https://lkml.kernel.org/r/578b03294708cc7258fad0dc9c2a2e809e5a8214.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>
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>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Vincenzo Frascino and committed by
Linus Torvalds
bad1e1c6 620954a6

+79 -4
-1
arch/arm64/include/asm/mte-def.h
··· 10 10 #define MTE_TAG_SHIFT 56 11 11 #define MTE_TAG_SIZE 4 12 12 #define MTE_TAG_MASK GENMASK((MTE_TAG_SHIFT + (MTE_TAG_SIZE - 1)), MTE_TAG_SHIFT) 13 - #define MTE_TAG_MAX (MTE_TAG_MASK >> MTE_TAG_SHIFT) 14 13 15 14 #endif /* __ASM_MTE_DEF_H */
+5
arch/arm64/include/asm/mte-kasan.h
··· 30 30 void *mte_set_mem_tag_range(void *addr, size_t size, u8 tag); 31 31 32 32 void mte_enable_kernel(void); 33 + void mte_init_tags(u64 max_tag); 33 34 34 35 #else /* CONFIG_ARM64_MTE */ 35 36 ··· 53 52 } 54 53 55 54 static inline void mte_enable_kernel(void) 55 + { 56 + } 57 + 58 + static inline void mte_init_tags(u64 max_tag) 56 59 { 57 60 } 58 61
+2
arch/arm64/include/asm/mte.h
··· 18 18 19 19 #include <asm/pgtable-types.h> 20 20 21 + extern u64 gcr_kernel_excl; 22 + 21 23 void mte_clear_page_tags(void *addr); 22 24 unsigned long mte_copy_tags_from_user(void *to, const void __user *from, 23 25 unsigned long n);
+3
arch/arm64/kernel/asm-offsets.c
··· 47 47 DEFINE(THREAD_KEYS_USER, offsetof(struct task_struct, thread.keys_user)); 48 48 DEFINE(THREAD_KEYS_KERNEL, offsetof(struct task_struct, thread.keys_kernel)); 49 49 #endif 50 + #ifdef CONFIG_ARM64_MTE 51 + DEFINE(THREAD_GCR_EL1_USER, offsetof(struct task_struct, thread.gcr_user_excl)); 52 + #endif 50 53 BLANK(); 51 54 DEFINE(S_X0, offsetof(struct pt_regs, regs[0])); 52 55 DEFINE(S_X2, offsetof(struct pt_regs, regs[2]));
+41
arch/arm64/kernel/entry.S
··· 173 173 #endif 174 174 .endm 175 175 176 + .macro mte_set_gcr, tmp, tmp2 177 + #ifdef CONFIG_ARM64_MTE 178 + /* 179 + * Calculate and set the exclude mask preserving 180 + * the RRND (bit[16]) setting. 181 + */ 182 + mrs_s \tmp2, SYS_GCR_EL1 183 + bfi \tmp2, \tmp, #0, #16 184 + msr_s SYS_GCR_EL1, \tmp2 185 + isb 186 + #endif 187 + .endm 188 + 189 + .macro mte_set_kernel_gcr, tmp, tmp2 190 + #ifdef CONFIG_KASAN_HW_TAGS 191 + alternative_if_not ARM64_MTE 192 + b 1f 193 + alternative_else_nop_endif 194 + ldr_l \tmp, gcr_kernel_excl 195 + 196 + mte_set_gcr \tmp, \tmp2 197 + 1: 198 + #endif 199 + .endm 200 + 201 + .macro mte_set_user_gcr, tsk, tmp, tmp2 202 + #ifdef CONFIG_ARM64_MTE 203 + alternative_if_not ARM64_MTE 204 + b 1f 205 + alternative_else_nop_endif 206 + ldr \tmp, [\tsk, #THREAD_GCR_EL1_USER] 207 + 208 + mte_set_gcr \tmp, \tmp2 209 + 1: 210 + #endif 211 + .endm 212 + 176 213 .macro kernel_entry, el, regsize = 64 177 214 .if \regsize == 32 178 215 mov w0, w0 // zero upper 32 bits of x0 ··· 248 211 apply_ssbd 1, x22, x23 249 212 250 213 ptrauth_keys_install_kernel tsk, x20, x22, x23 214 + 215 + mte_set_kernel_gcr x22, x23 251 216 252 217 scs_load tsk, x20 253 218 .else ··· 353 314 354 315 /* No kernel C function calls after this as user keys are set. */ 355 316 ptrauth_keys_install_user tsk, x0, x1, x2 317 + 318 + mte_set_user_gcr tsk, x0, x1 356 319 357 320 apply_ssbd 0, x0, x1 358 321 .endif
+28 -3
arch/arm64/kernel/mte.c
··· 23 23 #include <asm/ptrace.h> 24 24 #include <asm/sysreg.h> 25 25 26 + u64 gcr_kernel_excl __ro_after_init; 27 + 26 28 static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap) 27 29 { 28 30 pte_t old_pte = READ_ONCE(*ptep); ··· 131 129 return ptr; 132 130 } 133 131 132 + void mte_init_tags(u64 max_tag) 133 + { 134 + static bool gcr_kernel_excl_initialized; 135 + 136 + if (!gcr_kernel_excl_initialized) { 137 + /* 138 + * The format of the tags in KASAN is 0xFF and in MTE is 0xF. 139 + * This conversion extracts an MTE tag from a KASAN tag. 140 + */ 141 + u64 incl = GENMASK(FIELD_GET(MTE_TAG_MASK >> MTE_TAG_SHIFT, 142 + max_tag), 0); 143 + 144 + gcr_kernel_excl = ~incl & SYS_GCR_EL1_EXCL_MASK; 145 + gcr_kernel_excl_initialized = true; 146 + } 147 + 148 + /* Enable the kernel exclude mask for random tags generation. */ 149 + write_sysreg_s(SYS_GCR_EL1_RRND | gcr_kernel_excl, SYS_GCR_EL1); 150 + } 151 + 134 152 void mte_enable_kernel(void) 135 153 { 136 154 /* Enable MTE Sync Mode for EL1. */ ··· 193 171 static void set_gcr_el1_excl(u64 excl) 194 172 { 195 173 current->thread.gcr_user_excl = excl; 196 - update_gcr_el1_excl(excl); 174 + 175 + /* 176 + * SYS_GCR_EL1 will be set to current->thread.gcr_user_excl value 177 + * by mte_set_user_gcr() in kernel_exit, 178 + */ 197 179 } 198 180 199 181 void flush_mte_state(void) ··· 223 197 /* avoid expensive SCTLR_EL1 accesses if no change */ 224 198 if (current->thread.sctlr_tcf0 != next->thread.sctlr_tcf0) 225 199 update_sctlr_el1_tcf0(next->thread.sctlr_tcf0); 226 - update_gcr_el1_excl(next->thread.gcr_user_excl); 227 200 } 228 201 229 202 void mte_suspend_exit(void) ··· 230 205 if (!system_supports_mte()) 231 206 return; 232 207 233 - update_gcr_el1_excl(current->thread.gcr_user_excl); 208 + update_gcr_el1_excl(gcr_kernel_excl); 234 209 } 235 210 236 211 long set_mte_ctrl(struct task_struct *task, unsigned long arg)