1 /*- 2 * Copyright (c) 2021 The FreeBSD Foundation 3 * 4 * This software was developed by Andrew Turner under sponsorship from 5 * the FreeBSD Foundation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * This manages pointer authentication. As it needs to enable the use of 31 * pointer authentication and change the keys we must built this with 32 * pointer authentication disabled. 33 */ 34 #ifdef __ARM_FEATURE_PAC_DEFAULT 35 #error Must be built with pointer authentication disabled 36 #endif 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/param.h> 42 #include <sys/kernel.h> 43 #include <sys/libkern.h> 44 #include <sys/proc.h> 45 #include <sys/reboot.h> 46 47 #include <machine/armreg.h> 48 #include <machine/cpu.h> 49 #include <machine/reg.h> 50 #include <machine/vmparam.h> 51 52 #define SCTLR_PTRAUTH (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB) 53 54 static bool __read_mostly enable_ptrauth = false; 55 56 /* Functions called from assembly. */ 57 void ptrauth_start(void); 58 struct thread *ptrauth_switch(struct thread *); 59 void ptrauth_exit_el0(struct thread *); 60 void ptrauth_enter_el0(struct thread *); 61 62 static bool 63 ptrauth_disable(void) 64 { 65 const char *family, *maker, *product; 66 67 family = kern_getenv("smbios.system.family"); 68 maker = kern_getenv("smbios.system.maker"); 69 product = kern_getenv("smbios.system.product"); 70 if (family == NULL || maker == NULL || product == NULL) 71 return (false); 72 73 /* 74 * The Dev Kit appears to be configured to trap upon access to PAC 75 * registers, but the kernel boots at EL1 and so we have no way to 76 * inspect or change this configuration. As a workaround, simply 77 * disable PAC on this platform. 78 */ 79 if (strcmp(maker, "Microsoft Corporation") == 0 && 80 strcmp(family, "Surface") == 0 && 81 strcmp(product, "Windows Dev Kit 2023") == 0) 82 return (true); 83 84 return (false); 85 } 86 87 void 88 ptrauth_init(void) 89 { 90 uint64_t isar1; 91 int pac_enable; 92 93 /* 94 * Allow the sysadmin to disable pointer authentication globally, 95 * e.g. on broken hardware. 96 */ 97 pac_enable = 1; 98 TUNABLE_INT_FETCH("hw.pac.enable", &pac_enable); 99 if (!pac_enable) { 100 if (boothowto & RB_VERBOSE) 101 printf("Pointer authentication is disabled\n"); 102 return; 103 } 104 105 if (!get_kernel_reg(ID_AA64ISAR1_EL1, &isar1)) 106 return; 107 108 if (ptrauth_disable()) 109 return; 110 111 /* 112 * This assumes if there is pointer authentication on the boot CPU 113 * it will also be available on any non-boot CPUs. If this is ever 114 * not the case we will have to add a quirk. 115 */ 116 if (ID_AA64ISAR1_APA_VAL(isar1) > 0 || 117 ID_AA64ISAR1_API_VAL(isar1) > 0) { 118 enable_ptrauth = true; 119 elf64_addr_mask.code |= PAC_ADDR_MASK; 120 elf64_addr_mask.data |= PAC_ADDR_MASK; 121 } 122 } 123 124 /* Copy the keys when forking a new process */ 125 void 126 ptrauth_fork(struct thread *new_td, struct thread *orig_td) 127 { 128 if (!enable_ptrauth) 129 return; 130 131 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user, 132 sizeof(new_td->td_md.md_ptrauth_user)); 133 } 134 135 /* Generate new userspace keys when executing a new process */ 136 void 137 ptrauth_exec(struct thread *td) 138 { 139 if (!enable_ptrauth) 140 return; 141 142 arc4rand(&td->td_md.md_ptrauth_user, sizeof(td->td_md.md_ptrauth_user), 143 0); 144 } 145 146 /* 147 * Copy the user keys when creating a new userspace thread until it's clear 148 * how the ABI expects the various keys to be assigned. 149 */ 150 void 151 ptrauth_copy_thread(struct thread *new_td, struct thread *orig_td) 152 { 153 if (!enable_ptrauth) 154 return; 155 156 memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user, 157 sizeof(new_td->td_md.md_ptrauth_user)); 158 } 159 160 /* Generate new kernel keys when executing a new kernel thread */ 161 void 162 ptrauth_thread_alloc(struct thread *td) 163 { 164 if (!enable_ptrauth) 165 return; 166 167 arc4rand(&td->td_md.md_ptrauth_kern, sizeof(td->td_md.md_ptrauth_kern), 168 0); 169 } 170 171 /* 172 * Load the userspace keys. We can't use WRITE_SPECIALREG as we need 173 * to set the architecture extension. 174 */ 175 #define LOAD_KEY(space, name) \ 176 __asm __volatile( \ 177 ".arch_extension pauth \n" \ 178 "msr "#name"keylo_el1, %0 \n" \ 179 "msr "#name"keyhi_el1, %1 \n" \ 180 ".arch_extension nopauth \n" \ 181 :: "r"(td->td_md.md_ptrauth_##space.name.pa_key_lo), \ 182 "r"(td->td_md.md_ptrauth_##space.name.pa_key_hi)) 183 184 void 185 ptrauth_thread0(struct thread *td) 186 { 187 if (!enable_ptrauth) 188 return; 189 190 /* TODO: Generate a random number here */ 191 memset(&td->td_md.md_ptrauth_kern, 0, 192 sizeof(td->td_md.md_ptrauth_kern)); 193 LOAD_KEY(kern, apia); 194 /* 195 * No isb as this is called before ptrauth_start so can rely on 196 * the instruction barrier there. 197 */ 198 } 199 200 /* 201 * Enable pointer authentication. After this point userspace and the kernel 202 * can sign return addresses, etc. based on their keys 203 * 204 * This assumes either all or no CPUs have pointer authentication support, 205 * and, if supported, all CPUs have the same algorithm. 206 */ 207 void 208 ptrauth_start(void) 209 { 210 uint64_t sctlr; 211 212 if (!enable_ptrauth) 213 return; 214 215 /* Enable pointer authentication */ 216 sctlr = READ_SPECIALREG(sctlr_el1); 217 sctlr |= SCTLR_PTRAUTH; 218 WRITE_SPECIALREG(sctlr_el1, sctlr); 219 isb(); 220 } 221 222 #ifdef SMP 223 void 224 ptrauth_mp_start(uint64_t cpu) 225 { 226 struct ptrauth_key start_key; 227 uint64_t sctlr; 228 229 if (!enable_ptrauth) 230 return; 231 232 /* 233 * We need a key until we call sched_throw, however we don't have 234 * a thread until then. Create a key just for use within 235 * init_secondary and whatever it calls. As init_secondary never 236 * returns it is safe to do so from within it. 237 * 238 * As it's only used for a short length of time just use the cpu 239 * as the key. 240 */ 241 start_key.pa_key_lo = cpu; 242 start_key.pa_key_hi = ~cpu; 243 244 __asm __volatile( 245 ".arch_extension pauth \n" 246 "msr apiakeylo_el1, %0 \n" 247 "msr apiakeyhi_el1, %1 \n" 248 ".arch_extension nopauth \n" 249 :: "r"(start_key.pa_key_lo), "r"(start_key.pa_key_hi)); 250 251 /* Enable pointer authentication */ 252 sctlr = READ_SPECIALREG(sctlr_el1); 253 sctlr |= SCTLR_PTRAUTH; 254 WRITE_SPECIALREG(sctlr_el1, sctlr); 255 isb(); 256 } 257 #endif 258 259 struct thread * 260 ptrauth_switch(struct thread *td) 261 { 262 if (enable_ptrauth) { 263 LOAD_KEY(kern, apia); 264 isb(); 265 } 266 267 return (td); 268 } 269 270 /* Called when we are exiting uerspace and entering the kernel */ 271 void 272 ptrauth_exit_el0(struct thread *td) 273 { 274 if (!enable_ptrauth) 275 return; 276 277 LOAD_KEY(kern, apia); 278 isb(); 279 } 280 281 /* Called when we are about to exit the kernel and enter userspace */ 282 void 283 ptrauth_enter_el0(struct thread *td) 284 { 285 if (!enable_ptrauth) 286 return; 287 288 LOAD_KEY(user, apia); 289 LOAD_KEY(user, apib); 290 LOAD_KEY(user, apda); 291 LOAD_KEY(user, apdb); 292 LOAD_KEY(user, apga); 293 /* 294 * No isb as this is called from the exception handler so can rely 295 * on the eret instruction to be the needed context synchronizing event. 296 */ 297 } 298