]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/ptrauth.c
zfs: merge openzfs/zfs@797f55ef1
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / ptrauth.c
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 #include <sys/param.h>
40 #include <sys/kernel.h>
41 #include <sys/libkern.h>
42 #include <sys/proc.h>
43 #include <sys/reboot.h>
44
45 #include <machine/armreg.h>
46 #include <machine/cpu.h>
47 #include <machine/reg.h>
48 #include <machine/vmparam.h>
49
50 #define SCTLR_PTRAUTH   (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)
51
52 static bool __read_mostly enable_ptrauth = false;
53
54 /* Functions called from assembly. */
55 void ptrauth_start(void);
56 struct thread *ptrauth_switch(struct thread *);
57 void ptrauth_exit_el0(struct thread *);
58 void ptrauth_enter_el0(struct thread *);
59
60 static bool
61 ptrauth_disable(void)
62 {
63         const char *family, *maker, *product;
64
65         family = kern_getenv("smbios.system.family");
66         maker = kern_getenv("smbios.system.maker");
67         product = kern_getenv("smbios.system.product");
68         if (family == NULL || maker == NULL || product == NULL)
69                 return (false);
70
71         /*
72          * The Dev Kit appears to be configured to trap upon access to PAC
73          * registers, but the kernel boots at EL1 and so we have no way to
74          * inspect or change this configuration.  As a workaround, simply
75          * disable PAC on this platform.
76          */
77         if (strcmp(maker, "Microsoft Corporation") == 0 &&
78             strcmp(family, "Surface") == 0 &&
79             strcmp(product, "Windows Dev Kit 2023") == 0)
80                 return (true);
81
82         return (false);
83 }
84
85 void
86 ptrauth_init(void)
87 {
88         uint64_t isar1;
89         int pac_enable;
90
91         /*
92          * Allow the sysadmin to disable pointer authentication globally,
93          * e.g. on broken hardware.
94          */
95         pac_enable = 1;
96         TUNABLE_INT_FETCH("hw.pac.enable", &pac_enable);
97         if (!pac_enable) {
98                 if (boothowto & RB_VERBOSE)
99                         printf("Pointer authentication is disabled\n");
100                 return;
101         }
102
103         if (!get_kernel_reg(ID_AA64ISAR1_EL1, &isar1))
104                 return;
105
106         if (ptrauth_disable())
107                 return;
108
109         /*
110          * This assumes if there is pointer authentication on the boot CPU
111          * it will also be available on any non-boot CPUs. If this is ever
112          * not the case we will have to add a quirk.
113          */
114         if (ID_AA64ISAR1_APA_VAL(isar1) > 0 ||
115             ID_AA64ISAR1_API_VAL(isar1) > 0) {
116                 enable_ptrauth = true;
117                 elf64_addr_mask.code |= PAC_ADDR_MASK;
118                 elf64_addr_mask.data |= PAC_ADDR_MASK;
119         }
120 }
121
122 /* Copy the keys when forking a new process */
123 void
124 ptrauth_fork(struct thread *new_td, struct thread *orig_td)
125 {
126         if (!enable_ptrauth)
127                 return;
128
129         memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
130             sizeof(new_td->td_md.md_ptrauth_user));
131 }
132
133 /* Generate new userspace keys when executing a new process */
134 void
135 ptrauth_exec(struct thread *td)
136 {
137         if (!enable_ptrauth)
138                 return;
139
140         arc4rand(&td->td_md.md_ptrauth_user, sizeof(td->td_md.md_ptrauth_user),
141             0);
142 }
143
144 /*
145  * Copy the user keys when creating a new userspace thread until it's clear
146  * how the ABI expects the various keys to be assigned.
147  */
148 void
149 ptrauth_copy_thread(struct thread *new_td, struct thread *orig_td)
150 {
151         if (!enable_ptrauth)
152                 return;
153
154         memcpy(&new_td->td_md.md_ptrauth_user, &orig_td->td_md.md_ptrauth_user,
155             sizeof(new_td->td_md.md_ptrauth_user));
156 }
157
158 /* Generate new kernel keys when executing a new kernel thread */
159 void
160 ptrauth_thread_alloc(struct thread *td)
161 {
162         if (!enable_ptrauth)
163                 return;
164
165         arc4rand(&td->td_md.md_ptrauth_kern, sizeof(td->td_md.md_ptrauth_kern),
166             0);
167 }
168
169 /*
170  * Load the userspace keys. We can't use WRITE_SPECIALREG as we need
171  * to set the architecture extension.
172  */
173 #define LOAD_KEY(space, name)                                   \
174 __asm __volatile(                                               \
175         ".arch_extension pauth                  \n"             \
176         "msr    "#name"keylo_el1, %0            \n"             \
177         "msr    "#name"keyhi_el1, %1            \n"             \
178         ".arch_extension nopauth                \n"             \
179         :: "r"(td->td_md.md_ptrauth_##space.name.pa_key_lo),    \
180            "r"(td->td_md.md_ptrauth_##space.name.pa_key_hi))
181
182 void
183 ptrauth_thread0(struct thread *td)
184 {
185         if (!enable_ptrauth)
186                 return;
187
188         /* TODO: Generate a random number here */
189         memset(&td->td_md.md_ptrauth_kern, 0,
190             sizeof(td->td_md.md_ptrauth_kern));
191         LOAD_KEY(kern, apia);
192         /*
193          * No isb as this is called before ptrauth_start so can rely on
194          * the instruction barrier there.
195          */
196 }
197
198 /*
199  * Enable pointer authentication. After this point userspace and the kernel
200  * can sign return addresses, etc. based on their keys
201  *
202  * This assumes either all or no CPUs have pointer authentication support,
203  * and, if supported, all CPUs have the same algorithm.
204  */
205 void
206 ptrauth_start(void)
207 {
208         uint64_t sctlr;
209
210         if (!enable_ptrauth)
211                 return;
212
213         /* Enable pointer authentication */
214         sctlr = READ_SPECIALREG(sctlr_el1);
215         sctlr |= SCTLR_PTRAUTH;
216         WRITE_SPECIALREG(sctlr_el1, sctlr);
217         isb();
218 }
219
220 #ifdef SMP
221 void
222 ptrauth_mp_start(uint64_t cpu)
223 {
224         struct ptrauth_key start_key;
225         uint64_t sctlr;
226
227         if (!enable_ptrauth)
228                 return;
229
230         /*
231          * We need a key until we call sched_throw, however we don't have
232          * a thread until then. Create a key just for use within
233          * init_secondary and whatever it calls. As init_secondary never
234          * returns it is safe to do so from within it.
235          *
236          * As it's only used for a short length of time just use the cpu
237          * as the key.
238          */
239         start_key.pa_key_lo = cpu;
240         start_key.pa_key_hi = ~cpu;
241
242         __asm __volatile(
243             ".arch_extension pauth              \n"
244             "msr        apiakeylo_el1, %0       \n"
245             "msr        apiakeyhi_el1, %1       \n"
246             ".arch_extension nopauth            \n"
247             :: "r"(start_key.pa_key_lo), "r"(start_key.pa_key_hi));
248
249         /* Enable pointer authentication */
250         sctlr = READ_SPECIALREG(sctlr_el1);
251         sctlr |= SCTLR_PTRAUTH;
252         WRITE_SPECIALREG(sctlr_el1, sctlr);
253         isb();
254 }
255 #endif
256
257 struct thread *
258 ptrauth_switch(struct thread *td)
259 {
260         if (enable_ptrauth) {
261                 LOAD_KEY(kern, apia);
262                 isb();
263         }
264
265         return (td);
266 }
267
268 /* Called when we are exiting uerspace and entering the kernel */
269 void
270 ptrauth_exit_el0(struct thread *td)
271 {
272         if (!enable_ptrauth)
273                 return;
274
275         LOAD_KEY(kern, apia);
276         isb();
277 }
278
279 /* Called when we are about to exit the kernel and enter userspace */
280 void
281 ptrauth_enter_el0(struct thread *td)
282 {
283         if (!enable_ptrauth)
284                 return;
285
286         LOAD_KEY(user, apia);
287         LOAD_KEY(user, apib);
288         LOAD_KEY(user, apda);
289         LOAD_KEY(user, apdb);
290         LOAD_KEY(user, apga);
291         /*
292          * No isb as this is called from the exception handler so can rely
293          * on the eret instruction to be the needed context synchronizing event.
294          */
295 }