Commit | Line | Data |
---|---|---|
e754aedc DH |
1 | /* |
2 | * mpx-mini-test.c: routines to test Intel MPX (Memory Protection eXtentions) | |
3 | * | |
4 | * Written by: | |
5 | * "Ren, Qiaowei" <qiaowei.ren@intel.com> | |
6 | * "Wei, Gang" <gang.wei@intel.com> | |
7 | * "Hansen, Dave" <dave.hansen@intel.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms and conditions of the GNU General Public License, | |
11 | * version 2. | |
12 | */ | |
13 | ||
14 | /* | |
15 | * 2014-12-05: Dave Hansen: fixed all of the compiler warnings, and made sure | |
16 | * it works on 32-bit. | |
17 | */ | |
18 | ||
19 | int inspect_every_this_many_mallocs = 100; | |
20 | int zap_all_every_this_many_mallocs = 1000; | |
21 | ||
22 | #define _GNU_SOURCE | |
23 | #define _LARGEFILE64_SOURCE | |
24 | ||
25 | #include <string.h> | |
26 | #include <stdio.h> | |
27 | #include <stdint.h> | |
28 | #include <stdbool.h> | |
29 | #include <signal.h> | |
30 | #include <assert.h> | |
31 | #include <stdlib.h> | |
32 | #include <ucontext.h> | |
33 | #include <sys/mman.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/stat.h> | |
36 | #include <fcntl.h> | |
37 | #include <unistd.h> | |
38 | ||
39 | #include "mpx-hw.h" | |
40 | #include "mpx-debug.h" | |
41 | #include "mpx-mm.h" | |
42 | ||
43 | #ifndef __always_inline | |
44 | #define __always_inline inline __attribute__((always_inline) | |
45 | #endif | |
46 | ||
47 | #ifndef TEST_DURATION_SECS | |
48 | #define TEST_DURATION_SECS 3 | |
49 | #endif | |
50 | ||
51 | void write_int_to(char *prefix, char *file, int int_to_write) | |
52 | { | |
53 | char buf[100]; | |
54 | int fd = open(file, O_RDWR); | |
55 | int len; | |
56 | int ret; | |
57 | ||
58 | assert(fd >= 0); | |
59 | len = snprintf(buf, sizeof(buf), "%s%d", prefix, int_to_write); | |
60 | assert(len >= 0); | |
61 | assert(len < sizeof(buf)); | |
62 | ret = write(fd, buf, len); | |
63 | assert(ret == len); | |
64 | ret = close(fd); | |
65 | assert(!ret); | |
66 | } | |
67 | ||
68 | void write_pid_to(char *prefix, char *file) | |
69 | { | |
70 | write_int_to(prefix, file, getpid()); | |
71 | } | |
72 | ||
73 | void trace_me(void) | |
74 | { | |
75 | /* tracing events dir */ | |
76 | #define TED "/sys/kernel/debug/tracing/events/" | |
77 | /* | |
78 | write_pid_to("common_pid=", TED "signal/filter"); | |
79 | write_pid_to("common_pid=", TED "exceptions/filter"); | |
80 | write_int_to("", TED "signal/enable", 1); | |
81 | write_int_to("", TED "exceptions/enable", 1); | |
82 | */ | |
83 | write_pid_to("", "/sys/kernel/debug/tracing/set_ftrace_pid"); | |
84 | write_int_to("", "/sys/kernel/debug/tracing/trace", 0); | |
85 | } | |
86 | ||
87 | #define test_failed() __test_failed(__FILE__, __LINE__) | |
88 | static void __test_failed(char *f, int l) | |
89 | { | |
90 | fprintf(stderr, "abort @ %s::%d\n", f, l); | |
91 | abort(); | |
92 | } | |
93 | ||
94 | /* Error Printf */ | |
95 | #define eprintf(args...) fprintf(stderr, args) | |
96 | ||
97 | #ifdef __i386__ | |
98 | ||
99 | /* i386 directory size is 4MB */ | |
100 | #define REG_IP_IDX REG_EIP | |
101 | #define REX_PREFIX | |
102 | ||
103 | #define XSAVE_OFFSET_IN_FPMEM sizeof(struct _libc_fpstate) | |
104 | ||
105 | /* | |
106 | * __cpuid() is from the Linux Kernel: | |
107 | */ | |
108 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, | |
109 | unsigned int *ecx, unsigned int *edx) | |
110 | { | |
111 | /* ecx is often an input as well as an output. */ | |
112 | asm volatile( | |
113 | "push %%ebx;" | |
114 | "cpuid;" | |
115 | "mov %%ebx, %1;" | |
116 | "pop %%ebx" | |
117 | : "=a" (*eax), | |
118 | "=g" (*ebx), | |
119 | "=c" (*ecx), | |
120 | "=d" (*edx) | |
121 | : "0" (*eax), "2" (*ecx)); | |
122 | } | |
123 | ||
124 | #else /* __i386__ */ | |
125 | ||
126 | #define REG_IP_IDX REG_RIP | |
127 | #define REX_PREFIX "0x48, " | |
128 | ||
129 | #define XSAVE_OFFSET_IN_FPMEM 0 | |
130 | ||
131 | /* | |
132 | * __cpuid() is from the Linux Kernel: | |
133 | */ | |
134 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, | |
135 | unsigned int *ecx, unsigned int *edx) | |
136 | { | |
137 | /* ecx is often an input as well as an output. */ | |
138 | asm volatile( | |
139 | "cpuid;" | |
140 | : "=a" (*eax), | |
141 | "=b" (*ebx), | |
142 | "=c" (*ecx), | |
143 | "=d" (*edx) | |
144 | : "0" (*eax), "2" (*ecx)); | |
145 | } | |
146 | ||
147 | #endif /* !__i386__ */ | |
148 | ||
149 | struct xsave_hdr_struct { | |
150 | uint64_t xstate_bv; | |
151 | uint64_t reserved1[2]; | |
152 | uint64_t reserved2[5]; | |
153 | } __attribute__((packed)); | |
154 | ||
155 | struct bndregs_struct { | |
156 | uint64_t bndregs[8]; | |
157 | } __attribute__((packed)); | |
158 | ||
159 | struct bndcsr_struct { | |
160 | uint64_t cfg_reg_u; | |
161 | uint64_t status_reg; | |
162 | } __attribute__((packed)); | |
163 | ||
164 | struct xsave_struct { | |
165 | uint8_t fpu_sse[512]; | |
166 | struct xsave_hdr_struct xsave_hdr; | |
167 | uint8_t ymm[256]; | |
168 | uint8_t lwp[128]; | |
169 | struct bndregs_struct bndregs; | |
170 | struct bndcsr_struct bndcsr; | |
171 | } __attribute__((packed)); | |
172 | ||
173 | uint8_t __attribute__((__aligned__(64))) buffer[4096]; | |
174 | struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer; | |
175 | ||
176 | uint8_t __attribute__((__aligned__(64))) test_buffer[4096]; | |
177 | struct xsave_struct *xsave_test_buf = (struct xsave_struct *)test_buffer; | |
178 | ||
179 | uint64_t num_bnd_chk; | |
180 | ||
181 | static __always_inline void xrstor_state(struct xsave_struct *fx, uint64_t mask) | |
182 | { | |
183 | uint32_t lmask = mask; | |
184 | uint32_t hmask = mask >> 32; | |
185 | ||
186 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t" | |
187 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
188 | : "memory"); | |
189 | } | |
190 | ||
191 | static __always_inline void xsave_state_1(void *_fx, uint64_t mask) | |
192 | { | |
193 | uint32_t lmask = mask; | |
194 | uint32_t hmask = mask >> 32; | |
195 | unsigned char *fx = _fx; | |
196 | ||
197 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" | |
198 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
199 | : "memory"); | |
200 | } | |
201 | ||
202 | static inline uint64_t xgetbv(uint32_t index) | |
203 | { | |
204 | uint32_t eax, edx; | |
205 | ||
206 | asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */ | |
207 | : "=a" (eax), "=d" (edx) | |
208 | : "c" (index)); | |
209 | return eax + ((uint64_t)edx << 32); | |
210 | } | |
211 | ||
212 | static uint64_t read_mpx_status_sig(ucontext_t *uctxt) | |
213 | { | |
214 | memset(buffer, 0, sizeof(buffer)); | |
215 | memcpy(buffer, | |
216 | (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM, | |
217 | sizeof(struct xsave_struct)); | |
218 | ||
219 | return xsave_buf->bndcsr.status_reg; | |
220 | } | |
221 | ||
222 | #include <pthread.h> | |
223 | ||
224 | static uint8_t *get_next_inst_ip(uint8_t *addr) | |
225 | { | |
226 | uint8_t *ip = addr; | |
227 | uint8_t sib; | |
228 | uint8_t rm; | |
229 | uint8_t mod; | |
230 | uint8_t base; | |
231 | uint8_t modrm; | |
232 | ||
233 | /* determine the prefix. */ | |
234 | switch(*ip) { | |
235 | case 0xf2: | |
236 | case 0xf3: | |
237 | case 0x66: | |
238 | ip++; | |
239 | break; | |
240 | } | |
241 | ||
242 | /* look for rex prefix */ | |
243 | if ((*ip & 0x40) == 0x40) | |
244 | ip++; | |
245 | ||
246 | /* Make sure we have a MPX instruction. */ | |
247 | if (*ip++ != 0x0f) | |
248 | return addr; | |
249 | ||
250 | /* Skip the op code byte. */ | |
251 | ip++; | |
252 | ||
253 | /* Get the modrm byte. */ | |
254 | modrm = *ip++; | |
255 | ||
256 | /* Break it down into parts. */ | |
257 | rm = modrm & 7; | |
258 | mod = (modrm >> 6); | |
259 | ||
260 | /* Init the parts of the address mode. */ | |
261 | base = 8; | |
262 | ||
263 | /* Is it a mem mode? */ | |
264 | if (mod != 3) { | |
265 | /* look for scaled indexed addressing */ | |
266 | if (rm == 4) { | |
267 | /* SIB addressing */ | |
268 | sib = *ip++; | |
269 | base = sib & 7; | |
270 | switch (mod) { | |
271 | case 0: | |
272 | if (base == 5) | |
273 | ip += 4; | |
274 | break; | |
275 | ||
276 | case 1: | |
277 | ip++; | |
278 | break; | |
279 | ||
280 | case 2: | |
281 | ip += 4; | |
282 | break; | |
283 | } | |
284 | ||
285 | } else { | |
286 | /* MODRM addressing */ | |
287 | switch (mod) { | |
288 | case 0: | |
289 | /* DISP32 addressing, no base */ | |
290 | if (rm == 5) | |
291 | ip += 4; | |
292 | break; | |
293 | ||
294 | case 1: | |
295 | ip++; | |
296 | break; | |
297 | ||
298 | case 2: | |
299 | ip += 4; | |
300 | break; | |
301 | } | |
302 | } | |
303 | } | |
304 | return ip; | |
305 | } | |
306 | ||
307 | #ifdef si_lower | |
308 | static inline void *__si_bounds_lower(siginfo_t *si) | |
309 | { | |
310 | return si->si_lower; | |
311 | } | |
312 | ||
313 | static inline void *__si_bounds_upper(siginfo_t *si) | |
314 | { | |
315 | return si->si_upper; | |
316 | } | |
317 | #else | |
961888b1 RW |
318 | |
319 | /* | |
320 | * This deals with old version of _sigfault in some distros: | |
321 | * | |
322 | ||
323 | old _sigfault: | |
324 | struct { | |
325 | void *si_addr; | |
326 | } _sigfault; | |
327 | ||
328 | new _sigfault: | |
329 | struct { | |
330 | void __user *_addr; | |
331 | int _trapno; | |
332 | short _addr_lsb; | |
333 | union { | |
334 | struct { | |
335 | void __user *_lower; | |
336 | void __user *_upper; | |
337 | } _addr_bnd; | |
338 | __u32 _pkey; | |
339 | }; | |
340 | } _sigfault; | |
341 | * | |
342 | */ | |
343 | ||
e754aedc DH |
344 | static inline void **__si_bounds_hack(siginfo_t *si) |
345 | { | |
346 | void *sigfault = &si->_sifields._sigfault; | |
347 | void *end_sigfault = sigfault + sizeof(si->_sifields._sigfault); | |
961888b1 RW |
348 | int *trapno = (int*)end_sigfault; |
349 | /* skip _trapno and _addr_lsb */ | |
350 | void **__si_lower = (void**)(trapno + 2); | |
e754aedc DH |
351 | |
352 | return __si_lower; | |
353 | } | |
354 | ||
355 | static inline void *__si_bounds_lower(siginfo_t *si) | |
356 | { | |
357 | return *__si_bounds_hack(si); | |
358 | } | |
359 | ||
360 | static inline void *__si_bounds_upper(siginfo_t *si) | |
361 | { | |
961888b1 | 362 | return *(__si_bounds_hack(si) + 1); |
e754aedc DH |
363 | } |
364 | #endif | |
365 | ||
366 | static int br_count; | |
367 | static int expected_bnd_index = -1; | |
368 | uint64_t shadow_plb[NR_MPX_BOUNDS_REGISTERS][2]; /* shadow MPX bound registers */ | |
369 | unsigned long shadow_map[NR_MPX_BOUNDS_REGISTERS]; | |
370 | ||
371 | /* | |
372 | * The kernel is supposed to provide some information about the bounds | |
373 | * exception in the siginfo. It should match what we have in the bounds | |
374 | * registers that we are checking against. Just check against the shadow copy | |
375 | * since it is easily available, and we also check that *it* matches the real | |
376 | * registers. | |
377 | */ | |
378 | void check_siginfo_vs_shadow(siginfo_t* si) | |
379 | { | |
380 | int siginfo_ok = 1; | |
381 | void *shadow_lower = (void *)(unsigned long)shadow_plb[expected_bnd_index][0]; | |
382 | void *shadow_upper = (void *)(unsigned long)shadow_plb[expected_bnd_index][1]; | |
383 | ||
384 | if ((expected_bnd_index < 0) || | |
385 | (expected_bnd_index >= NR_MPX_BOUNDS_REGISTERS)) { | |
386 | fprintf(stderr, "ERROR: invalid expected_bnd_index: %d\n", | |
387 | expected_bnd_index); | |
388 | exit(6); | |
389 | } | |
390 | if (__si_bounds_lower(si) != shadow_lower) | |
391 | siginfo_ok = 0; | |
392 | if (__si_bounds_upper(si) != shadow_upper) | |
393 | siginfo_ok = 0; | |
394 | ||
395 | if (!siginfo_ok) { | |
396 | fprintf(stderr, "ERROR: siginfo bounds do not match " | |
397 | "shadow bounds for register %d\n", expected_bnd_index); | |
398 | exit(7); | |
399 | } | |
400 | } | |
401 | ||
402 | void handler(int signum, siginfo_t *si, void *vucontext) | |
403 | { | |
404 | int i; | |
405 | ucontext_t *uctxt = vucontext; | |
406 | int trapno; | |
407 | unsigned long ip; | |
408 | ||
409 | dprintf1("entered signal handler\n"); | |
410 | ||
411 | trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO]; | |
412 | ip = uctxt->uc_mcontext.gregs[REG_IP_IDX]; | |
413 | ||
414 | if (trapno == 5) { | |
415 | typeof(si->si_addr) *si_addr_ptr = &si->si_addr; | |
416 | uint64_t status = read_mpx_status_sig(uctxt); | |
417 | uint64_t br_reason = status & 0x3; | |
418 | ||
419 | br_count++; | |
420 | dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count); | |
421 | ||
d12fe87e | 422 | #define SEGV_BNDERR 3 /* failed address bound checks */ |
e754aedc DH |
423 | |
424 | dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n", | |
425 | status, ip, br_reason); | |
426 | dprintf2("si_signo: %d\n", si->si_signo); | |
427 | dprintf2(" signum: %d\n", signum); | |
428 | dprintf2("info->si_code == SEGV_BNDERR: %d\n", | |
429 | (si->si_code == SEGV_BNDERR)); | |
430 | dprintf2("info->si_code: %d\n", si->si_code); | |
431 | dprintf2("info->si_lower: %p\n", __si_bounds_lower(si)); | |
432 | dprintf2("info->si_upper: %p\n", __si_bounds_upper(si)); | |
433 | ||
e754aedc DH |
434 | for (i = 0; i < 8; i++) |
435 | dprintf3("[%d]: %p\n", i, si_addr_ptr[i]); | |
436 | switch (br_reason) { | |
437 | case 0: /* traditional BR */ | |
438 | fprintf(stderr, | |
439 | "Undefined status with bound exception:%jx\n", | |
440 | status); | |
441 | exit(5); | |
442 | case 1: /* #BR MPX bounds exception */ | |
443 | /* these are normal and we expect to see them */ | |
5f2173e0 JR |
444 | |
445 | check_siginfo_vs_shadow(si); | |
446 | ||
e754aedc DH |
447 | dprintf1("bounds exception (normal): status 0x%jx at %p si_addr: %p\n", |
448 | status, (void *)ip, si->si_addr); | |
449 | num_bnd_chk++; | |
450 | uctxt->uc_mcontext.gregs[REG_IP_IDX] = | |
451 | (greg_t)get_next_inst_ip((uint8_t *)ip); | |
452 | break; | |
453 | case 2: | |
454 | fprintf(stderr, "#BR status == 2, missing bounds table," | |
455 | "kernel should have handled!!\n"); | |
456 | exit(4); | |
457 | break; | |
458 | default: | |
459 | fprintf(stderr, "bound check error: status 0x%jx at %p\n", | |
460 | status, (void *)ip); | |
461 | num_bnd_chk++; | |
462 | uctxt->uc_mcontext.gregs[REG_IP_IDX] = | |
463 | (greg_t)get_next_inst_ip((uint8_t *)ip); | |
464 | fprintf(stderr, "bound check error: si_addr %p\n", si->si_addr); | |
465 | exit(3); | |
466 | } | |
467 | } else if (trapno == 14) { | |
468 | eprintf("ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n", | |
469 | trapno, ip); | |
470 | eprintf("si_addr %p\n", si->si_addr); | |
471 | eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | |
472 | test_failed(); | |
473 | } else { | |
474 | eprintf("unexpected trap %d! at 0x%lx\n", trapno, ip); | |
475 | eprintf("si_addr %p\n", si->si_addr); | |
476 | eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | |
477 | test_failed(); | |
478 | } | |
479 | } | |
480 | ||
481 | static inline void cpuid_count(unsigned int op, int count, | |
482 | unsigned int *eax, unsigned int *ebx, | |
483 | unsigned int *ecx, unsigned int *edx) | |
484 | { | |
485 | *eax = op; | |
486 | *ecx = count; | |
487 | __cpuid(eax, ebx, ecx, edx); | |
488 | } | |
489 | ||
490 | #define XSTATE_CPUID 0x0000000d | |
491 | ||
492 | /* | |
493 | * List of XSAVE features Linux knows about: | |
494 | */ | |
495 | enum xfeature_bit { | |
496 | XSTATE_BIT_FP, | |
497 | XSTATE_BIT_SSE, | |
498 | XSTATE_BIT_YMM, | |
499 | XSTATE_BIT_BNDREGS, | |
500 | XSTATE_BIT_BNDCSR, | |
501 | XSTATE_BIT_OPMASK, | |
502 | XSTATE_BIT_ZMM_Hi256, | |
503 | XSTATE_BIT_Hi16_ZMM, | |
504 | ||
505 | XFEATURES_NR_MAX, | |
506 | }; | |
507 | ||
508 | #define XSTATE_FP (1 << XSTATE_BIT_FP) | |
509 | #define XSTATE_SSE (1 << XSTATE_BIT_SSE) | |
510 | #define XSTATE_YMM (1 << XSTATE_BIT_YMM) | |
511 | #define XSTATE_BNDREGS (1 << XSTATE_BIT_BNDREGS) | |
512 | #define XSTATE_BNDCSR (1 << XSTATE_BIT_BNDCSR) | |
513 | #define XSTATE_OPMASK (1 << XSTATE_BIT_OPMASK) | |
514 | #define XSTATE_ZMM_Hi256 (1 << XSTATE_BIT_ZMM_Hi256) | |
515 | #define XSTATE_Hi16_ZMM (1 << XSTATE_BIT_Hi16_ZMM) | |
516 | ||
517 | #define MPX_XSTATES (XSTATE_BNDREGS | XSTATE_BNDCSR) /* 0x18 */ | |
518 | ||
519 | bool one_bit(unsigned int x, int bit) | |
520 | { | |
521 | return !!(x & (1<<bit)); | |
522 | } | |
523 | ||
524 | void print_state_component(int state_bit_nr, char *name) | |
525 | { | |
526 | unsigned int eax, ebx, ecx, edx; | |
527 | unsigned int state_component_size; | |
528 | unsigned int state_component_supervisor; | |
529 | unsigned int state_component_user; | |
530 | unsigned int state_component_aligned; | |
531 | ||
532 | /* See SDM Section 13.2 */ | |
533 | cpuid_count(XSTATE_CPUID, state_bit_nr, &eax, &ebx, &ecx, &edx); | |
534 | assert(eax || ebx || ecx); | |
535 | state_component_size = eax; | |
536 | state_component_supervisor = ((!ebx) && one_bit(ecx, 0)); | |
537 | state_component_user = !one_bit(ecx, 0); | |
538 | state_component_aligned = one_bit(ecx, 1); | |
539 | printf("%8s: size: %d user: %d supervisor: %d aligned: %d\n", | |
540 | name, | |
541 | state_component_size, state_component_user, | |
542 | state_component_supervisor, state_component_aligned); | |
543 | ||
544 | } | |
545 | ||
546 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx) */ | |
547 | #define XSAVE_FEATURE_BIT (26) /* XSAVE/XRSTOR/XSETBV/XGETBV */ | |
548 | #define OSXSAVE_FEATURE_BIT (27) /* XSAVE enabled in the OS */ | |
549 | ||
550 | bool check_mpx_support(void) | |
551 | { | |
552 | unsigned int eax, ebx, ecx, edx; | |
553 | ||
554 | cpuid_count(1, 0, &eax, &ebx, &ecx, &edx); | |
555 | ||
556 | /* We can't do much without XSAVE, so just make these assert()'s */ | |
557 | if (!one_bit(ecx, XSAVE_FEATURE_BIT)) { | |
558 | fprintf(stderr, "processor lacks XSAVE, can not run MPX tests\n"); | |
559 | exit(0); | |
560 | } | |
561 | ||
562 | if (!one_bit(ecx, OSXSAVE_FEATURE_BIT)) { | |
563 | fprintf(stderr, "processor lacks OSXSAVE, can not run MPX tests\n"); | |
564 | exit(0); | |
565 | } | |
566 | ||
567 | /* CPUs not supporting the XSTATE CPUID leaf do not support MPX */ | |
568 | /* Is this redundant with the feature bit checks? */ | |
569 | cpuid_count(0, 0, &eax, &ebx, &ecx, &edx); | |
570 | if (eax < XSTATE_CPUID) { | |
571 | fprintf(stderr, "processor lacks XSTATE CPUID leaf," | |
572 | " can not run MPX tests\n"); | |
573 | exit(0); | |
574 | } | |
575 | ||
576 | printf("XSAVE is supported by HW & OS\n"); | |
577 | ||
578 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); | |
579 | ||
580 | printf("XSAVE processor supported state mask: 0x%x\n", eax); | |
581 | printf("XSAVE OS supported state mask: 0x%jx\n", xgetbv(0)); | |
582 | ||
583 | /* Make sure that the MPX states are enabled in in XCR0 */ | |
584 | if ((eax & MPX_XSTATES) != MPX_XSTATES) { | |
585 | fprintf(stderr, "processor lacks MPX XSTATE(s), can not run MPX tests\n"); | |
586 | exit(0); | |
587 | } | |
588 | ||
589 | /* Make sure the MPX states are supported by XSAVE* */ | |
590 | if ((xgetbv(0) & MPX_XSTATES) != MPX_XSTATES) { | |
591 | fprintf(stderr, "MPX XSTATE(s) no enabled in XCR0, " | |
592 | "can not run MPX tests\n"); | |
593 | exit(0); | |
594 | } | |
595 | ||
596 | print_state_component(XSTATE_BIT_BNDREGS, "BNDREGS"); | |
597 | print_state_component(XSTATE_BIT_BNDCSR, "BNDCSR"); | |
598 | ||
599 | return true; | |
600 | } | |
601 | ||
602 | void enable_mpx(void *l1base) | |
603 | { | |
604 | /* enable point lookup */ | |
605 | memset(buffer, 0, sizeof(buffer)); | |
606 | xrstor_state(xsave_buf, 0x18); | |
607 | ||
608 | xsave_buf->xsave_hdr.xstate_bv = 0x10; | |
609 | xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base | 1; | |
610 | xsave_buf->bndcsr.status_reg = 0; | |
611 | ||
612 | dprintf2("bf xrstor\n"); | |
613 | dprintf2("xsave cndcsr: status %jx, configu %jx\n", | |
614 | xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u); | |
615 | xrstor_state(xsave_buf, 0x18); | |
616 | dprintf2("after xrstor\n"); | |
617 | ||
618 | xsave_state_1(xsave_buf, 0x18); | |
619 | ||
620 | dprintf1("xsave bndcsr: status %jx, configu %jx\n", | |
621 | xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u); | |
622 | } | |
623 | ||
624 | #include <sys/prctl.h> | |
625 | ||
626 | struct mpx_bounds_dir *bounds_dir_ptr; | |
627 | ||
628 | unsigned long __bd_incore(const char *func, int line) | |
629 | { | |
630 | unsigned long ret = nr_incore(bounds_dir_ptr, MPX_BOUNDS_DIR_SIZE_BYTES); | |
631 | return ret; | |
632 | } | |
633 | #define bd_incore() __bd_incore(__func__, __LINE__) | |
634 | ||
635 | void check_clear(void *ptr, unsigned long sz) | |
636 | { | |
637 | unsigned long *i; | |
638 | ||
639 | for (i = ptr; (void *)i < ptr + sz; i++) { | |
640 | if (*i) { | |
641 | dprintf1("%p is NOT clear at %p\n", ptr, i); | |
642 | assert(0); | |
643 | } | |
644 | } | |
645 | dprintf1("%p is clear for %lx\n", ptr, sz); | |
646 | } | |
647 | ||
648 | void check_clear_bd(void) | |
649 | { | |
650 | check_clear(bounds_dir_ptr, 2UL << 30); | |
651 | } | |
652 | ||
653 | #define USE_MALLOC_FOR_BOUNDS_DIR 1 | |
654 | bool process_specific_init(void) | |
655 | { | |
656 | unsigned long size; | |
657 | unsigned long *dir; | |
658 | /* Guarantee we have the space to align it, add padding: */ | |
659 | unsigned long pad = getpagesize(); | |
660 | ||
661 | size = 2UL << 30; /* 2GB */ | |
662 | if (sizeof(unsigned long) == 4) | |
663 | size = 4UL << 20; /* 4MB */ | |
664 | dprintf1("trying to allocate %ld MB bounds directory\n", (size >> 20)); | |
665 | ||
666 | if (USE_MALLOC_FOR_BOUNDS_DIR) { | |
667 | unsigned long _dir; | |
668 | ||
669 | dir = malloc(size + pad); | |
670 | assert(dir); | |
671 | _dir = (unsigned long)dir; | |
672 | _dir += 0xfffUL; | |
673 | _dir &= ~0xfffUL; | |
674 | dir = (void *)_dir; | |
675 | } else { | |
676 | /* | |
677 | * This makes debugging easier because the address | |
678 | * calculations are simpler: | |
679 | */ | |
680 | dir = mmap((void *)0x200000000000, size + pad, | |
681 | PROT_READ|PROT_WRITE, | |
682 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
683 | if (dir == (void *)-1) { | |
684 | perror("unable to allocate bounds directory"); | |
685 | abort(); | |
686 | } | |
687 | check_clear(dir, size); | |
688 | } | |
689 | bounds_dir_ptr = (void *)dir; | |
690 | madvise(bounds_dir_ptr, size, MADV_NOHUGEPAGE); | |
691 | bd_incore(); | |
692 | dprintf1("bounds directory: 0x%p -> 0x%p\n", bounds_dir_ptr, | |
693 | (char *)bounds_dir_ptr + size); | |
694 | check_clear(dir, size); | |
695 | enable_mpx(dir); | |
696 | check_clear(dir, size); | |
697 | if (prctl(43, 0, 0, 0, 0)) { | |
698 | printf("no MPX support\n"); | |
699 | abort(); | |
700 | return false; | |
701 | } | |
702 | return true; | |
703 | } | |
704 | ||
705 | bool process_specific_finish(void) | |
706 | { | |
707 | if (prctl(44)) { | |
708 | printf("no MPX support\n"); | |
709 | return false; | |
710 | } | |
711 | return true; | |
712 | } | |
713 | ||
714 | void setup_handler() | |
715 | { | |
716 | int r, rs; | |
717 | struct sigaction newact; | |
718 | struct sigaction oldact; | |
719 | ||
720 | /* #BR is mapped to sigsegv */ | |
721 | int signum = SIGSEGV; | |
722 | ||
723 | newact.sa_handler = 0; /* void(*)(int)*/ | |
724 | newact.sa_sigaction = handler; /* void (*)(int, siginfo_t*, void *) */ | |
725 | ||
726 | /*sigset_t - signals to block while in the handler */ | |
727 | /* get the old signal mask. */ | |
728 | rs = sigprocmask(SIG_SETMASK, 0, &newact.sa_mask); | |
729 | assert(rs == 0); | |
730 | ||
731 | /* call sa_sigaction, not sa_handler*/ | |
732 | newact.sa_flags = SA_SIGINFO; | |
733 | ||
734 | newact.sa_restorer = 0; /* void(*)(), obsolete */ | |
735 | r = sigaction(signum, &newact, &oldact); | |
736 | assert(r == 0); | |
737 | } | |
738 | ||
739 | void mpx_prepare(void) | |
740 | { | |
741 | dprintf2("%s()\n", __func__); | |
742 | setup_handler(); | |
743 | process_specific_init(); | |
744 | } | |
745 | ||
746 | void mpx_cleanup(void) | |
747 | { | |
748 | printf("%s(): %jd BRs. bye...\n", __func__, num_bnd_chk); | |
749 | process_specific_finish(); | |
750 | } | |
751 | ||
752 | /*-------------- the following is test case ---------------*/ | |
753 | #include <stdint.h> | |
754 | #include <stdbool.h> | |
755 | #include <stdlib.h> | |
756 | #include <stdio.h> | |
757 | #include <time.h> | |
758 | ||
759 | uint64_t num_lower_brs; | |
760 | uint64_t num_upper_brs; | |
761 | ||
762 | #define MPX_CONFIG_OFFSET 1024 | |
763 | #define MPX_BOUNDS_OFFSET 960 | |
764 | #define MPX_HEADER_OFFSET 512 | |
765 | #define MAX_ADDR_TESTED (1<<28) | |
766 | #define TEST_ROUNDS 100 | |
767 | ||
768 | /* | |
769 | 0F 1A /r BNDLDX-Load | |
770 | 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation | |
771 | 66 0F 1A /r BNDMOV bnd1, bnd2/m128 | |
772 | 66 0F 1B /r BNDMOV bnd1/m128, bnd2 | |
773 | F2 0F 1A /r BNDCU bnd, r/m64 | |
774 | F2 0F 1B /r BNDCN bnd, r/m64 | |
775 | F3 0F 1A /r BNDCL bnd, r/m64 | |
776 | F3 0F 1B /r BNDMK bnd, m64 | |
777 | */ | |
778 | ||
779 | static __always_inline void xsave_state(void *_fx, uint64_t mask) | |
780 | { | |
781 | uint32_t lmask = mask; | |
782 | uint32_t hmask = mask >> 32; | |
783 | unsigned char *fx = _fx; | |
784 | ||
785 | asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t" | |
786 | : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask) | |
787 | : "memory"); | |
788 | } | |
789 | ||
790 | static __always_inline void mpx_clear_bnd0(void) | |
791 | { | |
792 | long size = 0; | |
793 | void *ptr = NULL; | |
794 | /* F3 0F 1B /r BNDMK bnd, m64 */ | |
795 | /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */ | |
796 | asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t" | |
797 | : : "c" (ptr), "d" (size-1) | |
798 | : "memory"); | |
799 | } | |
800 | ||
801 | static __always_inline void mpx_make_bound_helper(unsigned long ptr, | |
802 | unsigned long size) | |
803 | { | |
804 | /* F3 0F 1B /r BNDMK bnd, m64 */ | |
805 | /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */ | |
806 | asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t" | |
807 | : : "c" (ptr), "d" (size-1) | |
808 | : "memory"); | |
809 | } | |
810 | ||
811 | static __always_inline void mpx_check_lowerbound_helper(unsigned long ptr) | |
812 | { | |
813 | /* F3 0F 1A /r NDCL bnd, r/m64 */ | |
814 | /* f3 0f 1a 01 bndcl (%rcx),%bnd0 */ | |
815 | asm volatile(".byte 0xf3,0x0f,0x1a,0x01\n\t" | |
816 | : : "c" (ptr) | |
817 | : "memory"); | |
818 | } | |
819 | ||
820 | static __always_inline void mpx_check_upperbound_helper(unsigned long ptr) | |
821 | { | |
822 | /* F2 0F 1A /r BNDCU bnd, r/m64 */ | |
823 | /* f2 0f 1a 01 bndcu (%rcx),%bnd0 */ | |
824 | asm volatile(".byte 0xf2,0x0f,0x1a,0x01\n\t" | |
825 | : : "c" (ptr) | |
826 | : "memory"); | |
827 | } | |
828 | ||
829 | static __always_inline void mpx_movbndreg_helper() | |
830 | { | |
831 | /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */ | |
832 | /* 66 0f 1b c2 bndmov %bnd0,%bnd2 */ | |
833 | ||
834 | asm volatile(".byte 0x66,0x0f,0x1b,0xc2\n\t"); | |
835 | } | |
836 | ||
837 | static __always_inline void mpx_movbnd2mem_helper(uint8_t *mem) | |
838 | { | |
839 | /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */ | |
840 | /* 66 0f 1b 01 bndmov %bnd0,(%rcx) */ | |
841 | asm volatile(".byte 0x66,0x0f,0x1b,0x01\n\t" | |
842 | : : "c" (mem) | |
843 | : "memory"); | |
844 | } | |
845 | ||
846 | static __always_inline void mpx_movbnd_from_mem_helper(uint8_t *mem) | |
847 | { | |
848 | /* 66 0F 1A /r BNDMOV bnd1, bnd2/m128 */ | |
849 | /* 66 0f 1a 01 bndmov (%rcx),%bnd0 */ | |
850 | asm volatile(".byte 0x66,0x0f,0x1a,0x01\n\t" | |
851 | : : "c" (mem) | |
852 | : "memory"); | |
853 | } | |
854 | ||
855 | static __always_inline void mpx_store_dsc_helper(unsigned long ptr_addr, | |
856 | unsigned long ptr_val) | |
857 | { | |
858 | /* 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation */ | |
859 | /* 0f 1b 04 11 bndstx %bnd0,(%rcx,%rdx,1) */ | |
860 | asm volatile(".byte 0x0f,0x1b,0x04,0x11\n\t" | |
861 | : : "c" (ptr_addr), "d" (ptr_val) | |
862 | : "memory"); | |
863 | } | |
864 | ||
865 | static __always_inline void mpx_load_dsc_helper(unsigned long ptr_addr, | |
866 | unsigned long ptr_val) | |
867 | { | |
868 | /* 0F 1A /r BNDLDX-Load */ | |
869 | /*/ 0f 1a 04 11 bndldx (%rcx,%rdx,1),%bnd0 */ | |
870 | asm volatile(".byte 0x0f,0x1a,0x04,0x11\n\t" | |
871 | : : "c" (ptr_addr), "d" (ptr_val) | |
872 | : "memory"); | |
873 | } | |
874 | ||
875 | void __print_context(void *__print_xsave_buffer, int line) | |
876 | { | |
877 | uint64_t *bounds = (uint64_t *)(__print_xsave_buffer + MPX_BOUNDS_OFFSET); | |
878 | uint64_t *cfg = (uint64_t *)(__print_xsave_buffer + MPX_CONFIG_OFFSET); | |
879 | ||
880 | int i; | |
881 | eprintf("%s()::%d\n", "print_context", line); | |
882 | for (i = 0; i < 4; i++) { | |
883 | eprintf("bound[%d]: 0x%016lx 0x%016lx(0x%016lx)\n", i, | |
884 | (unsigned long)bounds[i*2], | |
885 | ~(unsigned long)bounds[i*2+1], | |
886 | (unsigned long)bounds[i*2+1]); | |
887 | } | |
888 | ||
889 | eprintf("cpcfg: %jx cpstatus: %jx\n", cfg[0], cfg[1]); | |
890 | } | |
891 | #define print_context(x) __print_context(x, __LINE__) | |
892 | #ifdef DEBUG | |
893 | #define dprint_context(x) print_context(x) | |
894 | #else | |
895 | #define dprint_context(x) do{}while(0) | |
896 | #endif | |
897 | ||
898 | void init() | |
899 | { | |
900 | int i; | |
901 | ||
902 | srand((unsigned int)time(NULL)); | |
903 | ||
904 | for (i = 0; i < 4; i++) { | |
905 | shadow_plb[i][0] = 0; | |
906 | shadow_plb[i][1] = ~(unsigned long)0; | |
907 | } | |
908 | } | |
909 | ||
910 | long int __mpx_random(int line) | |
911 | { | |
912 | #ifdef NOT_SO_RANDOM | |
913 | static long fake = 722122311; | |
914 | fake += 563792075; | |
915 | return fakse; | |
916 | #else | |
917 | return random(); | |
918 | #endif | |
919 | } | |
920 | #define mpx_random() __mpx_random(__LINE__) | |
921 | ||
922 | uint8_t *get_random_addr() | |
923 | { | |
924 | uint8_t*addr = (uint8_t *)(unsigned long)(rand() % MAX_ADDR_TESTED); | |
925 | return (addr - (unsigned long)addr % sizeof(uint8_t *)); | |
926 | } | |
927 | ||
928 | static inline bool compare_context(void *__xsave_buffer) | |
929 | { | |
930 | uint64_t *bounds = (uint64_t *)(__xsave_buffer + MPX_BOUNDS_OFFSET); | |
931 | ||
932 | int i; | |
933 | for (i = 0; i < 4; i++) { | |
934 | dprintf3("shadow[%d]{%016lx/%016lx}\nbounds[%d]{%016lx/%016lx}\n", | |
935 | i, (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1], | |
936 | i, (unsigned long)bounds[i*2], ~(unsigned long)bounds[i*2+1]); | |
937 | if ((shadow_plb[i][0] != bounds[i*2]) || | |
938 | (shadow_plb[i][1] != ~(unsigned long)bounds[i*2+1])) { | |
939 | eprintf("ERROR comparing shadow to real bound register %d\n", i); | |
940 | eprintf("shadow{0x%016lx/0x%016lx}\nbounds{0x%016lx/0x%016lx}\n", | |
941 | (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1], | |
942 | (unsigned long)bounds[i*2], (unsigned long)bounds[i*2+1]); | |
943 | return false; | |
944 | } | |
945 | } | |
946 | ||
947 | return true; | |
948 | } | |
949 | ||
950 | void mkbnd_shadow(uint8_t *ptr, int index, long offset) | |
951 | { | |
952 | uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]); | |
953 | uint64_t *upper = (uint64_t *)&(shadow_plb[index][1]); | |
954 | *lower = (unsigned long)ptr; | |
955 | *upper = (unsigned long)ptr + offset - 1; | |
956 | } | |
957 | ||
958 | void check_lowerbound_shadow(uint8_t *ptr, int index) | |
959 | { | |
960 | uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]); | |
961 | if (*lower > (uint64_t)(unsigned long)ptr) | |
962 | num_lower_brs++; | |
963 | else | |
964 | dprintf1("LowerBoundChk passed:%p\n", ptr); | |
965 | } | |
966 | ||
967 | void check_upperbound_shadow(uint8_t *ptr, int index) | |
968 | { | |
969 | uint64_t upper = *(uint64_t *)&(shadow_plb[index][1]); | |
970 | if (upper < (uint64_t)(unsigned long)ptr) | |
971 | num_upper_brs++; | |
972 | else | |
973 | dprintf1("UpperBoundChk passed:%p\n", ptr); | |
974 | } | |
975 | ||
976 | __always_inline void movbndreg_shadow(int src, int dest) | |
977 | { | |
978 | shadow_plb[dest][0] = shadow_plb[src][0]; | |
979 | shadow_plb[dest][1] = shadow_plb[src][1]; | |
980 | } | |
981 | ||
982 | __always_inline void movbnd2mem_shadow(int src, unsigned long *dest) | |
983 | { | |
984 | unsigned long *lower = (unsigned long *)&(shadow_plb[src][0]); | |
985 | unsigned long *upper = (unsigned long *)&(shadow_plb[src][1]); | |
986 | *dest = *lower; | |
987 | *(dest+1) = *upper; | |
988 | } | |
989 | ||
990 | __always_inline void movbnd_from_mem_shadow(unsigned long *src, int dest) | |
991 | { | |
992 | unsigned long *lower = (unsigned long *)&(shadow_plb[dest][0]); | |
993 | unsigned long *upper = (unsigned long *)&(shadow_plb[dest][1]); | |
994 | *lower = *src; | |
995 | *upper = *(src+1); | |
996 | } | |
997 | ||
998 | __always_inline void stdsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val) | |
999 | { | |
1000 | shadow_map[0] = (unsigned long)shadow_plb[index][0]; | |
1001 | shadow_map[1] = (unsigned long)shadow_plb[index][1]; | |
1002 | shadow_map[2] = (unsigned long)ptr_val; | |
1003 | dprintf3("%s(%d, %p, %p) set shadow map[2]: %p\n", __func__, | |
1004 | index, ptr, ptr_val, ptr_val); | |
1005 | /*ptr ignored */ | |
1006 | } | |
1007 | ||
1008 | void lddsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val) | |
1009 | { | |
1010 | uint64_t lower = shadow_map[0]; | |
1011 | uint64_t upper = shadow_map[1]; | |
1012 | uint8_t *value = (uint8_t *)shadow_map[2]; | |
1013 | ||
1014 | if (value != ptr_val) { | |
1015 | dprintf2("%s(%d, %p, %p) init shadow bounds[%d] " | |
1016 | "because %p != %p\n", __func__, index, ptr, | |
1017 | ptr_val, index, value, ptr_val); | |
1018 | shadow_plb[index][0] = 0; | |
1019 | shadow_plb[index][1] = ~(unsigned long)0; | |
1020 | } else { | |
1021 | shadow_plb[index][0] = lower; | |
1022 | shadow_plb[index][1] = upper; | |
1023 | } | |
1024 | /* ptr ignored */ | |
1025 | } | |
1026 | ||
1027 | static __always_inline void mpx_test_helper0(uint8_t *buf, uint8_t *ptr) | |
1028 | { | |
1029 | mpx_make_bound_helper((unsigned long)ptr, 0x1800); | |
1030 | } | |
1031 | ||
1032 | static __always_inline void mpx_test_helper0_shadow(uint8_t *buf, uint8_t *ptr) | |
1033 | { | |
1034 | mkbnd_shadow(ptr, 0, 0x1800); | |
1035 | } | |
1036 | ||
1037 | static __always_inline void mpx_test_helper1(uint8_t *buf, uint8_t *ptr) | |
1038 | { | |
1039 | /* these are hard-coded to check bnd0 */ | |
1040 | expected_bnd_index = 0; | |
1041 | mpx_check_lowerbound_helper((unsigned long)(ptr-1)); | |
1042 | mpx_check_upperbound_helper((unsigned long)(ptr+0x1800)); | |
1043 | /* reset this since we do not expect any more bounds exceptions */ | |
1044 | expected_bnd_index = -1; | |
1045 | } | |
1046 | ||
1047 | static __always_inline void mpx_test_helper1_shadow(uint8_t *buf, uint8_t *ptr) | |
1048 | { | |
1049 | check_lowerbound_shadow(ptr-1, 0); | |
1050 | check_upperbound_shadow(ptr+0x1800, 0); | |
1051 | } | |
1052 | ||
1053 | static __always_inline void mpx_test_helper2(uint8_t *buf, uint8_t *ptr) | |
1054 | { | |
1055 | mpx_make_bound_helper((unsigned long)ptr, 0x1800); | |
1056 | mpx_movbndreg_helper(); | |
1057 | mpx_movbnd2mem_helper(buf); | |
1058 | mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800); | |
1059 | } | |
1060 | ||
1061 | static __always_inline void mpx_test_helper2_shadow(uint8_t *buf, uint8_t *ptr) | |
1062 | { | |
1063 | mkbnd_shadow(ptr, 0, 0x1800); | |
1064 | movbndreg_shadow(0, 2); | |
1065 | movbnd2mem_shadow(0, (unsigned long *)buf); | |
1066 | mkbnd_shadow(ptr+0x12, 0, 0x1800); | |
1067 | } | |
1068 | ||
1069 | static __always_inline void mpx_test_helper3(uint8_t *buf, uint8_t *ptr) | |
1070 | { | |
1071 | mpx_movbnd_from_mem_helper(buf); | |
1072 | } | |
1073 | ||
1074 | static __always_inline void mpx_test_helper3_shadow(uint8_t *buf, uint8_t *ptr) | |
1075 | { | |
1076 | movbnd_from_mem_shadow((unsigned long *)buf, 0); | |
1077 | } | |
1078 | ||
1079 | static __always_inline void mpx_test_helper4(uint8_t *buf, uint8_t *ptr) | |
1080 | { | |
1081 | mpx_store_dsc_helper((unsigned long)buf, (unsigned long)ptr); | |
1082 | mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800); | |
1083 | } | |
1084 | ||
1085 | static __always_inline void mpx_test_helper4_shadow(uint8_t *buf, uint8_t *ptr) | |
1086 | { | |
1087 | stdsc_shadow(0, buf, ptr); | |
1088 | mkbnd_shadow(ptr+0x12, 0, 0x1800); | |
1089 | } | |
1090 | ||
1091 | static __always_inline void mpx_test_helper5(uint8_t *buf, uint8_t *ptr) | |
1092 | { | |
1093 | mpx_load_dsc_helper((unsigned long)buf, (unsigned long)ptr); | |
1094 | } | |
1095 | ||
1096 | static __always_inline void mpx_test_helper5_shadow(uint8_t *buf, uint8_t *ptr) | |
1097 | { | |
1098 | lddsc_shadow(0, buf, ptr); | |
1099 | } | |
1100 | ||
1101 | #define NR_MPX_TEST_FUNCTIONS 6 | |
1102 | ||
1103 | /* | |
1104 | * For compatibility reasons, MPX will clear the bounds registers | |
1105 | * when you make function calls (among other things). We have to | |
1106 | * preserve the registers in between calls to the "helpers" since | |
1107 | * they build on each other. | |
1108 | * | |
1109 | * Be very careful not to make any function calls inside the | |
1110 | * helpers, or anywhere else beween the xrstor and xsave. | |
1111 | */ | |
1112 | #define run_helper(helper_nr, buf, buf_shadow, ptr) do { \ | |
1113 | xrstor_state(xsave_test_buf, flags); \ | |
1114 | mpx_test_helper##helper_nr(buf, ptr); \ | |
1115 | xsave_state(xsave_test_buf, flags); \ | |
1116 | mpx_test_helper##helper_nr##_shadow(buf_shadow, ptr); \ | |
1117 | } while (0) | |
1118 | ||
1119 | static void run_helpers(int nr, uint8_t *buf, uint8_t *buf_shadow, uint8_t *ptr) | |
1120 | { | |
1121 | uint64_t flags = 0x18; | |
1122 | ||
1123 | dprint_context(xsave_test_buf); | |
1124 | switch (nr) { | |
1125 | case 0: | |
1126 | run_helper(0, buf, buf_shadow, ptr); | |
1127 | break; | |
1128 | case 1: | |
1129 | run_helper(1, buf, buf_shadow, ptr); | |
1130 | break; | |
1131 | case 2: | |
1132 | run_helper(2, buf, buf_shadow, ptr); | |
1133 | break; | |
1134 | case 3: | |
1135 | run_helper(3, buf, buf_shadow, ptr); | |
1136 | break; | |
1137 | case 4: | |
1138 | run_helper(4, buf, buf_shadow, ptr); | |
1139 | break; | |
1140 | case 5: | |
1141 | run_helper(5, buf, buf_shadow, ptr); | |
1142 | break; | |
1143 | default: | |
1144 | test_failed(); | |
1145 | break; | |
1146 | } | |
1147 | dprint_context(xsave_test_buf); | |
1148 | } | |
1149 | ||
1150 | unsigned long buf_shadow[1024]; /* used to check load / store descriptors */ | |
1151 | extern long inspect_me(struct mpx_bounds_dir *bounds_dir); | |
1152 | ||
1153 | long cover_buf_with_bt_entries(void *buf, long buf_len) | |
1154 | { | |
1155 | int i; | |
1156 | long nr_to_fill; | |
1157 | int ratio = 1000; | |
1158 | unsigned long buf_len_in_ptrs; | |
1159 | ||
1160 | /* Fill about 1/100 of the space with bt entries */ | |
1161 | nr_to_fill = buf_len / (sizeof(unsigned long) * ratio); | |
1162 | ||
1163 | if (!nr_to_fill) | |
1164 | dprintf3("%s() nr_to_fill: %ld\n", __func__, nr_to_fill); | |
1165 | ||
1166 | /* Align the buffer to pointer size */ | |
1167 | while (((unsigned long)buf) % sizeof(void *)) { | |
1168 | buf++; | |
1169 | buf_len--; | |
1170 | } | |
1171 | /* We are storing pointers, so make */ | |
1172 | buf_len_in_ptrs = buf_len / sizeof(void *); | |
1173 | ||
1174 | for (i = 0; i < nr_to_fill; i++) { | |
1175 | long index = (mpx_random() % buf_len_in_ptrs); | |
1176 | void *ptr = buf + index * sizeof(unsigned long); | |
1177 | unsigned long ptr_addr = (unsigned long)ptr; | |
1178 | ||
1179 | /* ptr and size can be anything */ | |
1180 | mpx_make_bound_helper((unsigned long)ptr, 8); | |
1181 | ||
1182 | /* | |
1183 | * take bnd0 and put it in to bounds tables "buf + index" is an | |
1184 | * address inside the buffer where we are pretending that we | |
1185 | * are going to put a pointer We do not, though because we will | |
1186 | * never load entries from the table, so it doesn't matter. | |
1187 | */ | |
1188 | mpx_store_dsc_helper(ptr_addr, (unsigned long)ptr); | |
1189 | dprintf4("storing bound table entry for %lx (buf start @ %p)\n", | |
1190 | ptr_addr, buf); | |
1191 | } | |
1192 | return nr_to_fill; | |
1193 | } | |
1194 | ||
1195 | unsigned long align_down(unsigned long alignme, unsigned long align_to) | |
1196 | { | |
1197 | return alignme & ~(align_to-1); | |
1198 | } | |
1199 | ||
1200 | unsigned long align_up(unsigned long alignme, unsigned long align_to) | |
1201 | { | |
1202 | return (alignme + align_to - 1) & ~(align_to-1); | |
1203 | } | |
1204 | ||
1205 | /* | |
1206 | * Using 1MB alignment guarantees that each no allocation | |
1207 | * will overlap with another's bounds tables. | |
1208 | * | |
1209 | * We have to cook our own allocator here. malloc() can | |
1210 | * mix other allocation with ours which means that even | |
1211 | * if we free all of our allocations, there might still | |
1212 | * be bounds tables for the *areas* since there is other | |
1213 | * valid memory there. | |
1214 | * | |
1215 | * We also can't use malloc() because a free() of an area | |
1216 | * might not free it back to the kernel. We want it | |
1217 | * completely unmapped an malloc() does not guarantee | |
1218 | * that. | |
1219 | */ | |
1220 | #ifdef __i386__ | |
1221 | long alignment = 4096; | |
1222 | long sz_alignment = 4096; | |
1223 | #else | |
1224 | long alignment = 1 * MB; | |
1225 | long sz_alignment = 1 * MB; | |
1226 | #endif | |
1227 | void *mpx_mini_alloc(unsigned long sz) | |
1228 | { | |
1229 | unsigned long long tries = 0; | |
1230 | static void *last; | |
1231 | void *ptr; | |
1232 | void *try_at; | |
1233 | ||
1234 | sz = align_up(sz, sz_alignment); | |
1235 | ||
1236 | try_at = last + alignment; | |
1237 | while (1) { | |
1238 | ptr = mmap(try_at, sz, PROT_READ|PROT_WRITE, | |
1239 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
1240 | if (ptr == (void *)-1) | |
1241 | return NULL; | |
1242 | if (ptr == try_at) | |
1243 | break; | |
1244 | ||
1245 | munmap(ptr, sz); | |
1246 | try_at += alignment; | |
1247 | #ifdef __i386__ | |
1248 | /* | |
1249 | * This isn't quite correct for 32-bit binaries | |
1250 | * on 64-bit kernels since they can use the | |
1251 | * entire 32-bit address space, but it's close | |
1252 | * enough. | |
1253 | */ | |
1254 | if (try_at > (void *)0xC0000000) | |
1255 | #else | |
1256 | if (try_at > (void *)0x0000800000000000) | |
1257 | #endif | |
1258 | try_at = (void *)0x0; | |
1259 | if (!(++tries % 10000)) | |
1260 | dprintf1("stuck in %s(), tries: %lld\n", __func__, tries); | |
1261 | continue; | |
1262 | } | |
1263 | last = ptr; | |
1264 | dprintf3("mpx_mini_alloc(0x%lx) returning: %p\n", sz, ptr); | |
1265 | return ptr; | |
1266 | } | |
1267 | void mpx_mini_free(void *ptr, long sz) | |
1268 | { | |
1269 | dprintf2("%s() ptr: %p\n", __func__, ptr); | |
1270 | if ((unsigned long)ptr > 0x100000000000) { | |
1271 | dprintf1("uh oh !!!!!!!!!!!!!!! pointer too high: %p\n", ptr); | |
1272 | test_failed(); | |
1273 | } | |
1274 | sz = align_up(sz, sz_alignment); | |
1275 | dprintf3("%s() ptr: %p before munmap\n", __func__, ptr); | |
1276 | munmap(ptr, sz); | |
1277 | dprintf3("%s() ptr: %p DONE\n", __func__, ptr); | |
1278 | } | |
1279 | ||
1280 | #define NR_MALLOCS 100 | |
1281 | struct one_malloc { | |
1282 | char *ptr; | |
1283 | int nr_filled_btes; | |
1284 | unsigned long size; | |
1285 | }; | |
1286 | struct one_malloc mallocs[NR_MALLOCS]; | |
1287 | ||
1288 | void free_one_malloc(int index) | |
1289 | { | |
1290 | unsigned long free_ptr; | |
1291 | unsigned long mask; | |
1292 | ||
1293 | if (!mallocs[index].ptr) | |
1294 | return; | |
1295 | ||
1296 | mpx_mini_free(mallocs[index].ptr, mallocs[index].size); | |
1297 | dprintf4("freed[%d]: %p\n", index, mallocs[index].ptr); | |
1298 | ||
1299 | free_ptr = (unsigned long)mallocs[index].ptr; | |
1300 | mask = alignment-1; | |
1301 | dprintf4("lowerbits: %lx / %lx mask: %lx\n", free_ptr, | |
1302 | (free_ptr & mask), mask); | |
1303 | assert((free_ptr & mask) == 0); | |
1304 | ||
1305 | mallocs[index].ptr = NULL; | |
1306 | } | |
1307 | ||
1308 | #ifdef __i386__ | |
1309 | #define MPX_BOUNDS_TABLE_COVERS 4096 | |
1310 | #else | |
1311 | #define MPX_BOUNDS_TABLE_COVERS (1 * MB) | |
1312 | #endif | |
1313 | void zap_everything(void) | |
1314 | { | |
1315 | long after_zap; | |
1316 | long before_zap; | |
1317 | int i; | |
1318 | ||
1319 | before_zap = inspect_me(bounds_dir_ptr); | |
1320 | dprintf1("zapping everything start: %ld\n", before_zap); | |
1321 | for (i = 0; i < NR_MALLOCS; i++) | |
1322 | free_one_malloc(i); | |
1323 | ||
1324 | after_zap = inspect_me(bounds_dir_ptr); | |
1325 | dprintf1("zapping everything done: %ld\n", after_zap); | |
1326 | /* | |
1327 | * We only guarantee to empty the thing out if our allocations are | |
1328 | * exactly aligned on the boundaries of a boudns table. | |
1329 | */ | |
1330 | if ((alignment >= MPX_BOUNDS_TABLE_COVERS) && | |
1331 | (sz_alignment >= MPX_BOUNDS_TABLE_COVERS)) { | |
1332 | if (after_zap != 0) | |
1333 | test_failed(); | |
1334 | ||
1335 | assert(after_zap == 0); | |
1336 | } | |
1337 | } | |
1338 | ||
1339 | void do_one_malloc(void) | |
1340 | { | |
1341 | static int malloc_counter; | |
1342 | long sz; | |
1343 | int rand_index = (mpx_random() % NR_MALLOCS); | |
1344 | void *ptr = mallocs[rand_index].ptr; | |
1345 | ||
1346 | dprintf3("%s() enter\n", __func__); | |
1347 | ||
1348 | if (ptr) { | |
1349 | dprintf3("freeing one malloc at index: %d\n", rand_index); | |
1350 | free_one_malloc(rand_index); | |
1351 | if (mpx_random() % (NR_MALLOCS*3) == 3) { | |
1352 | int i; | |
1353 | dprintf3("zapping some more\n"); | |
1354 | for (i = rand_index; i < NR_MALLOCS; i++) | |
1355 | free_one_malloc(i); | |
1356 | } | |
1357 | if ((mpx_random() % zap_all_every_this_many_mallocs) == 4) | |
1358 | zap_everything(); | |
1359 | } | |
1360 | ||
1361 | /* 1->~1M */ | |
1362 | sz = (1 + mpx_random() % 1000) * 1000; | |
1363 | ptr = mpx_mini_alloc(sz); | |
1364 | if (!ptr) { | |
1365 | /* | |
1366 | * If we are failing allocations, just assume we | |
1367 | * are out of memory and zap everything. | |
1368 | */ | |
1369 | dprintf3("zapping everything because out of memory\n"); | |
1370 | zap_everything(); | |
1371 | goto out; | |
1372 | } | |
1373 | ||
1374 | dprintf3("malloc: %p size: 0x%lx\n", ptr, sz); | |
1375 | mallocs[rand_index].nr_filled_btes = cover_buf_with_bt_entries(ptr, sz); | |
1376 | mallocs[rand_index].ptr = ptr; | |
1377 | mallocs[rand_index].size = sz; | |
1378 | out: | |
1379 | if ((++malloc_counter) % inspect_every_this_many_mallocs == 0) | |
1380 | inspect_me(bounds_dir_ptr); | |
1381 | } | |
1382 | ||
1383 | void run_timed_test(void (*test_func)(void)) | |
1384 | { | |
1385 | int done = 0; | |
1386 | long iteration = 0; | |
1387 | static time_t last_print; | |
1388 | time_t now; | |
1389 | time_t start; | |
1390 | ||
1391 | time(&start); | |
1392 | while (!done) { | |
1393 | time(&now); | |
1394 | if ((now - start) > TEST_DURATION_SECS) | |
1395 | done = 1; | |
1396 | ||
1397 | test_func(); | |
1398 | iteration++; | |
1399 | ||
1400 | if ((now - last_print > 1) || done) { | |
1401 | printf("iteration %ld complete, OK so far\n", iteration); | |
1402 | last_print = now; | |
1403 | } | |
1404 | } | |
1405 | } | |
1406 | ||
1407 | void check_bounds_table_frees(void) | |
1408 | { | |
1409 | printf("executing unmaptest\n"); | |
1410 | inspect_me(bounds_dir_ptr); | |
1411 | run_timed_test(&do_one_malloc); | |
1412 | printf("done with malloc() fun\n"); | |
1413 | } | |
1414 | ||
1415 | void insn_test_failed(int test_nr, int test_round, void *buf, | |
1416 | void *buf_shadow, void *ptr) | |
1417 | { | |
1418 | print_context(xsave_test_buf); | |
1419 | eprintf("ERROR: test %d round %d failed\n", test_nr, test_round); | |
1420 | while (test_nr == 5) { | |
1421 | struct mpx_bt_entry *bte; | |
1422 | struct mpx_bounds_dir *bd = (void *)bounds_dir_ptr; | |
1423 | struct mpx_bd_entry *bde = mpx_vaddr_to_bd_entry(buf, bd); | |
1424 | ||
1425 | printf(" bd: %p\n", bd); | |
1426 | printf("&bde: %p\n", bde); | |
1427 | printf("*bde: %lx\n", *(unsigned long *)bde); | |
1428 | if (!bd_entry_valid(bde)) | |
1429 | break; | |
1430 | ||
1431 | bte = mpx_vaddr_to_bt_entry(buf, bd); | |
1432 | printf(" te: %p\n", bte); | |
1433 | printf("bte[0]: %lx\n", bte->contents[0]); | |
1434 | printf("bte[1]: %lx\n", bte->contents[1]); | |
1435 | printf("bte[2]: %lx\n", bte->contents[2]); | |
1436 | printf("bte[3]: %lx\n", bte->contents[3]); | |
1437 | break; | |
1438 | } | |
1439 | test_failed(); | |
1440 | } | |
1441 | ||
1442 | void check_mpx_insns_and_tables(void) | |
1443 | { | |
1444 | int successes = 0; | |
1445 | int failures = 0; | |
1446 | int buf_size = (1024*1024); | |
1447 | unsigned long *buf = malloc(buf_size); | |
1448 | const int total_nr_tests = NR_MPX_TEST_FUNCTIONS * TEST_ROUNDS; | |
1449 | int i, j; | |
1450 | ||
1451 | memset(buf, 0, buf_size); | |
1452 | memset(buf_shadow, 0, sizeof(buf_shadow)); | |
1453 | ||
1454 | for (i = 0; i < TEST_ROUNDS; i++) { | |
1455 | uint8_t *ptr = get_random_addr() + 8; | |
1456 | ||
1457 | for (j = 0; j < NR_MPX_TEST_FUNCTIONS; j++) { | |
1458 | if (0 && j != 5) { | |
1459 | successes++; | |
1460 | continue; | |
1461 | } | |
1462 | dprintf2("starting test %d round %d\n", j, i); | |
1463 | dprint_context(xsave_test_buf); | |
1464 | /* | |
1465 | * test5 loads an address from the bounds tables. | |
1466 | * The load will only complete if 'ptr' matches | |
1467 | * the load and the store, so with random addrs, | |
1468 | * the odds of this are very small. Make it | |
1469 | * higher by only moving 'ptr' 1/10 times. | |
1470 | */ | |
1471 | if (random() % 10 <= 0) | |
1472 | ptr = get_random_addr() + 8; | |
1473 | dprintf3("random ptr{%p}\n", ptr); | |
1474 | dprint_context(xsave_test_buf); | |
1475 | run_helpers(j, (void *)buf, (void *)buf_shadow, ptr); | |
1476 | dprint_context(xsave_test_buf); | |
1477 | if (!compare_context(xsave_test_buf)) { | |
1478 | insn_test_failed(j, i, buf, buf_shadow, ptr); | |
1479 | failures++; | |
1480 | goto exit; | |
1481 | } | |
1482 | successes++; | |
1483 | dprint_context(xsave_test_buf); | |
1484 | dprintf2("finished test %d round %d\n", j, i); | |
1485 | dprintf3("\n"); | |
1486 | dprint_context(xsave_test_buf); | |
1487 | } | |
1488 | } | |
1489 | ||
1490 | exit: | |
1491 | dprintf2("\nabout to free:\n"); | |
1492 | free(buf); | |
1493 | dprintf1("successes: %d\n", successes); | |
1494 | dprintf1(" failures: %d\n", failures); | |
1495 | dprintf1(" tests: %d\n", total_nr_tests); | |
1496 | dprintf1(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs); | |
1497 | dprintf1(" saw: %d #BRs\n", br_count); | |
1498 | if (failures) { | |
1499 | eprintf("ERROR: non-zero number of failures\n"); | |
1500 | exit(20); | |
1501 | } | |
1502 | if (successes != total_nr_tests) { | |
1503 | eprintf("ERROR: succeded fewer than number of tries (%d != %d)\n", | |
1504 | successes, total_nr_tests); | |
1505 | exit(21); | |
1506 | } | |
1507 | if (num_upper_brs + num_lower_brs != br_count) { | |
1508 | eprintf("ERROR: unexpected number of #BRs: %jd %jd %d\n", | |
1509 | num_upper_brs, num_lower_brs, br_count); | |
1510 | eprintf("successes: %d\n", successes); | |
1511 | eprintf(" failures: %d\n", failures); | |
1512 | eprintf(" tests: %d\n", total_nr_tests); | |
1513 | eprintf(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs); | |
1514 | eprintf(" saw: %d #BRs\n", br_count); | |
1515 | exit(22); | |
1516 | } | |
1517 | } | |
1518 | ||
1519 | /* | |
1520 | * This is supposed to SIGSEGV nicely once the kernel | |
1521 | * can no longer allocate vaddr space. | |
1522 | */ | |
1523 | void exhaust_vaddr_space(void) | |
1524 | { | |
1525 | unsigned long ptr; | |
1526 | /* Try to make sure there is no room for a bounds table anywhere */ | |
1527 | unsigned long skip = MPX_BOUNDS_TABLE_SIZE_BYTES - PAGE_SIZE; | |
1528 | #ifdef __i386__ | |
1529 | unsigned long max_vaddr = 0xf7788000UL; | |
1530 | #else | |
1531 | unsigned long max_vaddr = 0x800000000000UL; | |
1532 | #endif | |
1533 | ||
1534 | dprintf1("%s() start\n", __func__); | |
1535 | /* do not start at 0, we aren't allowed to map there */ | |
1536 | for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) { | |
1537 | void *ptr_ret; | |
1538 | int ret = madvise((void *)ptr, PAGE_SIZE, MADV_NORMAL); | |
1539 | ||
1540 | if (!ret) { | |
1541 | dprintf1("madvise() %lx ret: %d\n", ptr, ret); | |
1542 | continue; | |
1543 | } | |
1544 | ptr_ret = mmap((void *)ptr, PAGE_SIZE, PROT_READ|PROT_WRITE, | |
1545 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | |
1546 | if (ptr_ret != (void *)ptr) { | |
1547 | perror("mmap"); | |
1548 | dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret); | |
1549 | break; | |
1550 | } | |
1551 | if (!(ptr & 0xffffff)) | |
1552 | dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret); | |
1553 | } | |
1554 | for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) { | |
1555 | dprintf2("covering 0x%lx with bounds table entries\n", ptr); | |
1556 | cover_buf_with_bt_entries((void *)ptr, PAGE_SIZE); | |
1557 | } | |
1558 | dprintf1("%s() end\n", __func__); | |
1559 | printf("done with vaddr space fun\n"); | |
1560 | } | |
1561 | ||
1562 | void mpx_table_test(void) | |
1563 | { | |
1564 | printf("starting mpx bounds table test\n"); | |
1565 | run_timed_test(check_mpx_insns_and_tables); | |
1566 | printf("done with mpx bounds table test\n"); | |
1567 | } | |
1568 | ||
1569 | int main(int argc, char **argv) | |
1570 | { | |
1571 | int unmaptest = 0; | |
1572 | int vaddrexhaust = 0; | |
1573 | int tabletest = 0; | |
1574 | int i; | |
1575 | ||
1576 | check_mpx_support(); | |
1577 | mpx_prepare(); | |
1578 | srandom(11179); | |
1579 | ||
1580 | bd_incore(); | |
1581 | init(); | |
1582 | bd_incore(); | |
1583 | ||
1584 | trace_me(); | |
1585 | ||
1586 | xsave_state((void *)xsave_test_buf, 0x1f); | |
1587 | if (!compare_context(xsave_test_buf)) | |
1588 | printf("Init failed\n"); | |
1589 | ||
1590 | for (i = 1; i < argc; i++) { | |
1591 | if (!strcmp(argv[i], "unmaptest")) | |
1592 | unmaptest = 1; | |
1593 | if (!strcmp(argv[i], "vaddrexhaust")) | |
1594 | vaddrexhaust = 1; | |
1595 | if (!strcmp(argv[i], "tabletest")) | |
1596 | tabletest = 1; | |
1597 | } | |
1598 | if (!(unmaptest || vaddrexhaust || tabletest)) { | |
1599 | unmaptest = 1; | |
1600 | /* vaddrexhaust = 1; */ | |
1601 | tabletest = 1; | |
1602 | } | |
1603 | if (unmaptest) | |
1604 | check_bounds_table_frees(); | |
1605 | if (tabletest) | |
1606 | mpx_table_test(); | |
1607 | if (vaddrexhaust) | |
1608 | exhaust_vaddr_space(); | |
1609 | printf("%s completed successfully\n", argv[0]); | |
1610 | exit(0); | |
1611 | } | |
1612 | ||
1613 | #include "mpx-dig.c" |