x86_pte_tester.c revision 1.1 1 /* $NetBSD: x86_pte_tester.c,v 1.1 2020/04/26 09:08:41 maxv Exp $ */
2
3 /*
4 * Copyright (c) 2016 The NetBSD Foundation, Inc.
5 * All rights reserved.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define __HAVE_DIRECT_MAP
30 #define __HAVE_PCPU_AREA
31 #define SVS
32
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/module.h>
36 #include <sys/proc.h>
37 #include <sys/sysctl.h>
38 #include <uvm/uvm.h>
39 #include <x86/pmap.h>
40
41 #if defined(__x86_64__)
42 # include <amd64/pmap.h>
43 # define NLEVEL 4
44 #else
45 # error "Unsupported configuration"
46 #endif
47
48 static struct {
49 struct sysctllog *ctx_sysctllog;
50 vaddr_t levels[NLEVEL];
51 struct {
52 size_t l4;
53 size_t l3;
54 size_t l2;
55 size_t l1;
56 } coord;
57 struct {
58 size_t n_rwx;
59 bool kernel_map_with_low_ptes;
60 bool pte_is_user_accessible;
61 size_t n_user_space_is_kernel;
62 size_t n_kernel_space_is_user;
63 size_t n_svs_g_bit_set;
64 } results;
65 } tester_ctx;
66
67 typedef enum {
68 WALK_NEXT, /* go to the next level */
69 WALK_SKIP, /* skip the next level, but keep iterating on the current one */
70 WALK_STOP /* stop the iteration on the current level */
71 } walk_type;
72
73 /* -------------------------------------------------------------------------- */
74
75 #define is_flag(__ent, __flag) (((__ent) & __flag) != 0)
76 #define is_valid(__ent) is_flag(__ent, PTE_P)
77 #define get_pa(__pde) (__pde & PTE_FRAME)
78
79 #define L4_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t))
80 #define L3_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t))
81 #define L2_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t))
82 #define L1_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t))
83
84 static void
85 scan_l1(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl))
86 {
87 pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[0];
88 size_t i;
89
90 pmap_kenter_pa(tester_ctx.levels[0], pa, VM_PROT_READ, 0);
91 pmap_update(pmap_kernel());
92
93 for (i = 0; i < L1_MAX_NENTRIES; i++) {
94 tester_ctx.coord.l1 = i;
95 if (is_valid(pd[i])) {
96 fn(pd[i], i, 1);
97 }
98 }
99
100 pmap_kremove(tester_ctx.levels[0], PAGE_SIZE);
101 pmap_update(pmap_kernel());
102 }
103
104 static void
105 scan_l2(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl))
106 {
107 pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[1];
108 walk_type ret;
109 size_t i;
110
111 pmap_kenter_pa(tester_ctx.levels[1], pa, VM_PROT_READ, 0);
112 pmap_update(pmap_kernel());
113
114 for (i = 0; i < L2_MAX_NENTRIES; i++) {
115 tester_ctx.coord.l2 = i;
116 if (!is_valid(pd[i]))
117 continue;
118 ret = fn(pd[i], i, 2);
119 if (ret == WALK_STOP)
120 break;
121 if (is_flag(pd[i], PTE_PS))
122 continue;
123 if (ret == WALK_NEXT)
124 scan_l1(get_pa(pd[i]), fn);
125 }
126
127 pmap_kremove(tester_ctx.levels[1], PAGE_SIZE);
128 pmap_update(pmap_kernel());
129 }
130
131 static void
132 scan_l3(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl))
133 {
134 pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[2];
135 walk_type ret;
136 size_t i;
137
138 pmap_kenter_pa(tester_ctx.levels[2], pa, VM_PROT_READ, 0);
139 pmap_update(pmap_kernel());
140
141 for (i = 0; i < L3_MAX_NENTRIES; i++) {
142 tester_ctx.coord.l3 = i;
143 if (!is_valid(pd[i]))
144 continue;
145 ret = fn(pd[i], i, 3);
146 if (ret == WALK_STOP)
147 break;
148 if (is_flag(pd[i], PTE_PS))
149 continue;
150 if (ret == WALK_NEXT)
151 scan_l2(get_pa(pd[i]), fn);
152 }
153
154 pmap_kremove(tester_ctx.levels[2], PAGE_SIZE);
155 pmap_update(pmap_kernel());
156 }
157
158 static void
159 scan_l4(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl))
160 {
161 pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[3];
162 walk_type ret;
163 size_t i;
164
165 pmap_kenter_pa(tester_ctx.levels[3], pa, VM_PROT_READ, 0);
166 pmap_update(pmap_kernel());
167
168 for (i = 0; i < L4_MAX_NENTRIES; i++) {
169 tester_ctx.coord.l4 = i;
170 if (!is_valid(pd[i]))
171 continue;
172 ret = fn(pd[i], i, 4);
173 if (ret == WALK_STOP)
174 break;
175 if (is_flag(pd[i], PTE_PS))
176 continue;
177 if (ret == WALK_NEXT)
178 scan_l3(get_pa(pd[i]), fn);
179 }
180
181 pmap_kremove(tester_ctx.levels[3], PAGE_SIZE);
182 pmap_update(pmap_kernel());
183 }
184
185 static void
186 scan_tree(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl))
187 {
188 scan_l4(pa, fn);
189 }
190
191 /* -------------------------------------------------------------------------- */
192
193 /*
194 * Rule: the number of kernel RWX pages should be zero.
195 */
196 static walk_type
197 count_krwx(pd_entry_t pde, size_t slot, int lvl)
198 {
199 if (lvl == NLEVEL && slot < 256) {
200 return WALK_SKIP;
201 }
202 if (is_flag(pde, PTE_NX) || !is_flag(pde, PTE_W)) {
203 return WALK_SKIP;
204 }
205 if (lvl != 1 && !is_flag(pde, PTE_PS)) {
206 return WALK_NEXT;
207 }
208
209 if (lvl == 4) {
210 tester_ctx.results.n_rwx += (NBPD_L4 / PAGE_SIZE);
211 } else if (lvl == 3) {
212 tester_ctx.results.n_rwx += (NBPD_L3 / PAGE_SIZE);
213 } else if (lvl == 2) {
214 tester_ctx.results.n_rwx += (NBPD_L2 / PAGE_SIZE);
215 } else if (lvl == 1) {
216 tester_ctx.results.n_rwx += (NBPD_L1 / PAGE_SIZE);
217 }
218
219 return WALK_NEXT;
220 }
221
222 /*
223 * Rule: the lower half of the kernel map must be zero.
224 */
225 static walk_type
226 check_kernel_map(pd_entry_t pde, size_t slot, int lvl)
227 {
228 if (lvl != NLEVEL) {
229 return WALK_STOP;
230 }
231 if (slot >= 256) {
232 return WALK_SKIP;
233 }
234 if (pde != 0) {
235 tester_ctx.results.kernel_map_with_low_ptes |= true;
236 }
237 return WALK_SKIP;
238 }
239
240 /*
241 * Rule: the PTE space must not have user permissions.
242 */
243 static walk_type
244 check_pte_space(pd_entry_t pde, size_t slot, int lvl)
245 {
246 if (lvl != NLEVEL) {
247 return WALK_STOP;
248 }
249 if (slot != PDIR_SLOT_PTE) {
250 return WALK_SKIP;
251 }
252 if (is_flag(pde, PTE_U)) {
253 tester_ctx.results.pte_is_user_accessible |= true;
254 }
255 return WALK_SKIP;
256 }
257
258 /*
259 * Rule: each page in the lower half must have user permissions.
260 */
261 static walk_type
262 check_user_space(pd_entry_t pde, size_t slot, int lvl)
263 {
264 if (lvl == NLEVEL && slot >= 256) {
265 return WALK_SKIP;
266 }
267 if (!is_flag(pde, PTE_U)) {
268 tester_ctx.results.n_user_space_is_kernel += 1;
269 return WALK_SKIP;
270 }
271 return WALK_NEXT;
272 }
273
274 /*
275 * Rule: each page in the higher half must have kernel permissions.
276 */
277 static walk_type
278 check_kernel_space(pd_entry_t pde, size_t slot, int lvl)
279 {
280 if (lvl == NLEVEL && slot < 256) {
281 return WALK_SKIP;
282 }
283 if (lvl == NLEVEL && slot == PDIR_SLOT_PTE) {
284 return WALK_SKIP;
285 }
286 if (is_flag(pde, PTE_U)) {
287 tester_ctx.results.n_kernel_space_is_user += 1;
288 return WALK_SKIP;
289 }
290 return WALK_NEXT;
291 }
292
293 /*
294 * Rule: the SVS map is allowed to use the G bit only on the PCPU area.
295 */
296 static walk_type
297 check_svs_g_bit(pd_entry_t pde, size_t slot, int lvl)
298 {
299 if (lvl == NLEVEL && slot == PDIR_SLOT_PCPU) {
300 return WALK_SKIP;
301 }
302 if (is_flag(pde, PTE_G)) {
303 tester_ctx.results.n_svs_g_bit_set += 1;
304 return WALK_SKIP;
305 }
306 return WALK_NEXT;
307 }
308
309 /* -------------------------------------------------------------------------- */
310
311 static void
312 scan_svs(void)
313 {
314 extern bool svs_enabled;
315 paddr_t pa0;
316
317 if (!svs_enabled) {
318 tester_ctx.results.n_svs_g_bit_set = -1;
319 return;
320 }
321
322 kpreempt_disable();
323 pa0 = curcpu()->ci_svs_updirpa;
324 scan_tree(pa0, &check_user_space);
325 scan_tree(pa0, &check_kernel_space);
326 scan_tree(pa0, &check_svs_g_bit);
327 kpreempt_enable();
328 }
329
330 static void
331 scan_proc(struct proc *p)
332 {
333 struct pmap *pmap = p->p_vmspace->vm_map.pmap;
334 paddr_t pa0;
335
336 mutex_enter(&pmap->pm_lock);
337
338 kpreempt_disable();
339 pa0 = (paddr_t)pmap->pm_pdirpa[0];
340 scan_tree(pa0, &check_user_space);
341 scan_tree(pa0, &check_kernel_space);
342 scan_tree(pa0, &check_pte_space);
343 kpreempt_enable();
344
345 mutex_exit(&pmap->pm_lock);
346 }
347
348 static void
349 x86_pte_run_scans(void)
350 {
351 struct pmap *kpm = pmap_kernel();
352 paddr_t pa0;
353
354 memset(&tester_ctx.results, 0, sizeof(tester_ctx.results));
355
356 /* Scan the current user process. */
357 scan_proc(curproc);
358
359 /* Scan the SVS mapping. */
360 scan_svs();
361
362 /* Scan the kernel map. */
363 pa0 = (paddr_t)kpm->pm_pdirpa[0];
364 scan_tree(pa0, &count_krwx);
365 scan_tree(pa0, &check_kernel_map);
366 }
367
368 static void
369 x86_pte_levels_init(void)
370 {
371 size_t i;
372 for (i = 0; i < NLEVEL; i++) {
373 tester_ctx.levels[i] = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
374 UVM_KMF_VAONLY);
375 }
376 }
377
378 static void
379 x86_pte_levels_destroy(void)
380 {
381 size_t i;
382 for (i = 0; i < NLEVEL; i++) {
383 uvm_km_free(kernel_map, tester_ctx.levels[i], PAGE_SIZE,
384 UVM_KMF_VAONLY);
385 }
386 }
387
388 /* -------------------------------------------------------------------------- */
389
390 static int
391 x86_pte_sysctl_run(SYSCTLFN_ARGS)
392 {
393 if (oldlenp == NULL)
394 return EINVAL;
395
396 x86_pte_run_scans();
397
398 if (oldp == NULL) {
399 *oldlenp = sizeof(tester_ctx.results);
400 return 0;
401 }
402
403 if (*oldlenp < sizeof(tester_ctx.results))
404 return ENOMEM;
405
406 return copyout(&tester_ctx.results, oldp, sizeof(tester_ctx.results));
407 }
408
409 static int
410 x86_pte_sysctl_init(void)
411 {
412 struct sysctllog **log = &tester_ctx.ctx_sysctllog;
413 const struct sysctlnode *rnode, *cnode;
414 int error;
415
416 error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT,
417 CTLTYPE_NODE, "x86_pte_test",
418 SYSCTL_DESCR("x86_pte testing interface"),
419 NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL);
420 if (error)
421 goto out;
422
423 error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT,
424 CTLTYPE_STRUCT, "test",
425 SYSCTL_DESCR("execute a x86_pte test"),
426 x86_pte_sysctl_run, 0, NULL, 0, CTL_CREATE, CTL_EOL);
427
428 out:
429 if (error)
430 sysctl_teardown(log);
431 return error;
432 }
433
434 static void
435 x86_pte_sysctl_destroy(void)
436 {
437 sysctl_teardown(&tester_ctx.ctx_sysctllog);
438 }
439
440 /* -------------------------------------------------------------------------- */
441
442 MODULE(MODULE_CLASS_MISC, x86_pte_tester, NULL);
443
444 static int
445 x86_pte_tester_modcmd(modcmd_t cmd, void *arg __unused)
446 {
447 int error = 0;
448
449 switch (cmd) {
450 case MODULE_CMD_INIT:
451 x86_pte_levels_init();
452 error = x86_pte_sysctl_init();
453 break;
454 case MODULE_CMD_FINI:
455 x86_pte_sysctl_destroy();
456 x86_pte_levels_destroy();
457 break;
458 default:
459 error = ENOTTY;
460 break;
461 }
462
463 return error;
464 }
465